/*  GSsoft
 *  Copyright (C) 2002-2005  GSsoft Team
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <math.h>

#include "GS.h"
#include "Soft.h"
#include "Texts.h"
#include "Color.h"
#include "Mem.h"

#undef ABS
#define ABS(a) (((a)<0) ? -(a) : (a))

__inline int shl10idiv(int x, int y) {
	s64 bi = x;
	bi <<= 10;
	return (int)(bi / y);
}

__inline s64 shl10idiv64(s64 x, s64 y) {
	s64 bi = x;
	bi <<= 10;
	return (s64)(bi / y);
}

//

void drawLineF(Vertex * v) {
	int dx = v[1].x - v[0].x;
	int dy = v[1].y - v[0].y;
	int ax = ABS(dx) << 1;
	int ay = ABS(dy) << 1;
	int sx = (dx >= 0) ? 1 : -1;
	int sy = (dy >= 0) ? 1 : -1;
	int x = v[0].x;
	int y = v[0].y;

#ifdef GS_LOG
	GS_LOG("drawLineF %dx%d - %dx%d rgba=%x\n",
		   v[0].x, v[0].y, v[1].x, v[1].y, gs.rgba);
#endif

	SETdrawPixel();

	if (ax > ay) {
		int d = ay - (ax >> 1);
		while (x != v[1].x) {
			drawPixel(x, y, gs.rgba);

			if (d > 0 || (d == 0 && sx == 1)) {
				y += sy;
				d -= ax;
			}
			x += sx;
			d += ay;
		}
	} else {
		int d = ax - (ay >> 1);
		while (y != v[1].y) {
			drawPixel(x, y, gs.rgba);

			if (d > 0 || (d == 0 && sy == 1)) {
				x += sx;
				d -= ay;
			}
			y += sy;
			d += ax;
		}
	}
}

void drawLineF_F(Vertex * v) {
	int dx = v[1].x - v[0].x;
	int dy = v[1].y - v[0].y;
	int ax = ABS(dx) << 1;
	int ay = ABS(dy) << 1;
	int sx = (dx >= 0) ? 1 : -1;
	int sy = (dy >= 0) ? 1 : -1;
	int x = v[0].x;
	int y = v[0].y;
	u32 f0, f1;
	int df;

#ifdef GS_LOG
	GS_LOG("drawLineF_F %dx%d - %dx%d rgba=%x\n",
		   v[0].x, v[0].y, v[1].x, v[1].y, gs.rgba);
#endif

	SETdrawPixelF();

	f0 = v[0].f << 16;
	f1 = v[1].f << 16;

	if (ax > ay) {
		int d = ay - (ax >> 1);
		df = (f1 - f0) / dx;
		while (x != v[1].x) {
			drawPixelF(x, y, gs.rgba, f0 >> 16);

			if (d > 0 || (d == 0 && sx == 1)) {
				y += sy; d -= ax;
			}
			x += sx; d += ay;
			f0+= df; 
		}
	} else {
		int d = ax - (ay >> 1);
		df = (f1 - f0) / dy;
		while (y != v[1].y) {
			drawPixelF(x, y, gs.rgba, f0 >> 16);

			if (d > 0 || (d == 0 && sy == 1)) {
				x += sx; d -= ay;
			}
			y += sy; d += ax;
			f0+= df; 
		}
	}
}

void drawLineF_Z(Vertex * v) {
	int dx = v[1].x - v[0].x;
	int dy = v[1].y - v[0].y;
	int ax = ABS(dx) << 1;
	int ay = ABS(dy) << 1;
	int sx = (dx >= 0) ? 1 : -1;
	int sy = (dy >= 0) ? 1 : -1;
	int x = v[0].x;
	int y = v[0].y;

#ifdef GS_LOG
	GS_LOG("drawLineF_Z %dx%d - %dx%d rgba=%x\n",
		   v[0].x, v[0].y, v[1].x, v[1].y, gs.rgba);
#endif

	SETdrawPixelZ();

	if (ax > ay) {
		int d = ay - (ax >> 1);
		while (x != v[1].x) {
			drawPixelZ(x, y, gs.rgba, v[1].z);

			if (d > 0 || (d == 0 && sx == 1)) {
				y += sy; d -= ax;
			}
			x += sx; d += ay;
		}
	} else {
		int d = ax - (ay >> 1);
		while (y != v[1].y) {
			drawPixelZ(x, y, gs.rgba, v[1].z);

			if (d > 0 || (d == 0 && sy == 1)) {
				x += sx; d -= ay;
			}
			y += sy; d += ax;
		}
	}
}

void drawLineG(Vertex * v) {
	int dx = v[1].x - v[0].x;
	int dy = v[1].y - v[0].y;
	int ax = ABS(dx) << 1;
	int ay = ABS(dy) << 1;
	int sx = (dx >= 0) ? 1 : -1;
	int sy = (dy >= 0) ? 1 : -1;
	int x = v[0].x;
	int y = v[0].y;
	u32 r0, g0, b0, a0;
	u32 r1, g1, b1, a1;
	int dr, dg, db, da;

#ifdef GS_LOG
	GS_LOG("drawLineG %dx%d %x - %dx%d %x\n",
		   v[0].x, v[0].y, v[0].rgba, v[1].x, v[1].y, v[1].rgba);
#endif

	SETdrawPixel();

	r0 = (v[0].rgba & 0x00ff0000);
	g0 = (v[0].rgba & 0x0000ff00) << 8;
	b0 = (v[0].rgba & 0x000000ff) << 16;
	a0 = (v[0].rgba & 0xff000000) >> 8;
	r1 = (v[1].rgba & 0x00ff0000);
	g1 = (v[1].rgba & 0x0000ff00) << 8;
	b1 = (v[1].rgba & 0x000000ff) << 16;
	a1 = (v[1].rgba & 0xff000000) >> 8;

	if (ax > ay) {
		int d = ay - (ax >> 1);

		if (dx == 0) return;
		dr = ((int)r1 - (int)r0) / dx;
		dg = ((int)g1 - (int)g0) / dx;
		db = ((int)b1 - (int)b0) / dx;
		da = ((int)a1 - (int)a0) / dx;

		while (x != v[1].x) {
			drawPixel(x, y, ( r0 & 0xff0000)        | 
							((g0 & 0xff0000) >> 8)  | 
							((b0 & 0xff0000) >> 16) | 
							((a0 & 0xff0000) << 8));

			if (d > 0 || (d == 0 && sx == 1)) {
				y += sy;
				d -= ax;
			}
			x += sx; d += ay;
			r0+= dr; g0+= dg; b0+= db; a0+= da;
		}
	} else {
		int d = ax - (ay >> 1);

		if (dy == 0) return;
		dr = ((int)r1 - (int)r0) / dy;
		dg = ((int)g1 - (int)g0) / dy;
		db = ((int)b1 - (int)b0) / dy;
		da = ((int)a1 - (int)a0) / dy;

		while (y != v[1].y) {
			drawPixel(x, y, ( r0 & 0xff0000)        | 
							((g0 & 0xff0000) >> 8)  | 
							((b0 & 0xff0000) >> 16) |
							((a0 & 0xff0000) << 8));

			if (d > 0 || (d == 0 && sy == 1)) {
				x += sx;
				d -= ay;
			}
			y += sy; d += ax;
			r0+= dr; g0+= dg; b0+= db; a0+= da;
		}
	}
}

void drawLineG_Z(Vertex * v) {
	int dx = v[1].x - v[0].x;
	int dy = v[1].y - v[0].y;
	int ax = ABS(dx) << 1;
	int ay = ABS(dy) << 1;
	int sx = (dx >= 0) ? 1 : -1;
	int sy = (dy >= 0) ? 1 : -1;
	int x = v[0].x;
	int y = v[0].y;
	u32 r0, g0, b0, a0;
	u32 r1, g1, b1, a1;
	int dr, dg, db, da;

#ifdef GS_LOG
	GS_LOG("drawLineG_Z %dx%d %x - %dx%d %x\n",
		   v[0].x, v[0].y, v[0].rgba, v[1].x, v[1].y, v[1].rgba);
#endif

	SETdrawPixelZ();

	r0 = (v[0].rgba & 0x00ff0000);
	g0 = (v[0].rgba & 0x0000ff00) << 8;
	b0 = (v[0].rgba & 0x000000ff) << 16;
	a0 = (v[0].rgba & 0xff000000) >> 8;
	r1 = (v[1].rgba & 0x00ff0000);
	g1 = (v[1].rgba & 0x0000ff00) << 8;
	b1 = (v[1].rgba & 0x000000ff) << 16;
	a1 = (v[1].rgba & 0xff000000) >> 8;

	if (ax > ay) {
		int d = ay - (ax >> 1);

		if (dx == 0) return;
		dr = ((int)r1 - (int)r0) / dx;
		dg = ((int)g1 - (int)g0) / dx;
		db = ((int)b1 - (int)b0) / dx;
		da = ((int)a1 - (int)a0) / dx;

		while (x != v[1].x) {
			drawPixelZ(x, y, ( r0 & 0xff0000)        | 
							 ((g0 & 0xff0000) >> 8)  | 
							 ((b0 & 0xff0000) >> 16) |
							 ((a0 & 0xff0000) << 8), v[1].z);

			if (d > 0 || (d == 0 && sx == 1)) {
				y += sy;
				d -= ax;
			}
			x += sx; d += ay;
			r0+= dr; g0+= dg; b0+= db;
		}
	} else {
		int d = ax - (ay >> 1);

		if (dy == 0) return;
		dr = ((int)r1 - (int)r0) / dy;
		dg = ((int)g1 - (int)g0) / dy;
		db = ((int)b1 - (int)b0) / dy;
		da = ((int)a1 - (int)a0) / dy;

		while (y != v[1].y) {
			drawPixelZ(x, y, ( r0 & 0xff0000)        | 
							 ((g0 & 0xff0000) >> 8)  | 
							 ((b0 & 0xff0000) >> 16) |
							 ((a0 & 0xff0000) << 8), v[1].z);

			if (d > 0 || (d == 0 && sy == 1)) {
				x += sx;
				d -= ay;
			}
			y += sy; d += ax;
			r0+= dr; g0+= dg; b0+= db;
		}
	}
}

////////////////////////////////////////////////////////////////////////

void drawSprite(Vertex * v) {
	int x, y;

#ifdef GS_LOG
	GS_LOG("drawSprite %dx%d - %dx%d %lx\n",
		   v[0].x, v[0].y, v[1].x, v[1].y, gs.rgba);
#endif

	SETdrawPixel();
	for (y = v[0].y; y < v[1].y; y++)
		for (x = v[0].x; x < v[1].x; x++)
			drawPixel(x, y, gs.rgba);
}

void drawSprite_F(Vertex * v) {
	int x, y;

#ifdef GS_LOG
	GS_LOG("drawSprite_F %dx%d - %dx%d %lx\n",
		   v[0].x, v[0].y, v[1].x, v[1].y, gs.rgba);
#endif

	SETdrawPixelF();
	for (y = v[0].y; y < v[1].y; y++)
		for (x = v[0].x; x < v[1].x; x++)
			drawPixelF(x, y, gs.rgba, v[1].f);
}

void drawSprite_Z(Vertex * v) {
	int x, y;

#ifdef GS_LOG
	GS_LOG("drawSprite_Z %dx%d %lx - %dx%d %lx\n",
		   v[0].x, v[0].y, v[0].rgba, v[1].x, v[1].y, v[1].rgba);
#endif

	SETdrawPixelZ();

	for (y = v[0].y; y < v[1].y; y++) {
		for (x = v[0].x; x < v[1].x; x++) {
			drawPixelZ(x, y, gs.rgba, v[1].z);
		}
	}
}

void drawSprite_ZF(Vertex * v) {
	int x, y;

#ifdef GS_LOG
	GS_LOG("drawSprite_ZF %dx%d %lx - %dx%d %lx\n",
		   v[0].x, v[0].y, v[0].rgba, v[1].x, v[1].y, v[1].rgba);
#endif

	SETdrawPixelZF();

	for (y = v[0].y; y < v[1].y; y++) {
		for (x = v[0].x; x < v[1].x; x++) {
			drawPixelZF(x, y, gs.rgba, v[1].z, v[1].f);
		}
	}
}

////////////////////////////////////////////////////////////////////////
// TEXTURED SPRITE
////////////////////////////////////////////////////////////////////////


#define _drawSpriteT(ftx) \
	SetTexture(); \
	SETdrawPixel(); \
 \
	if (vx[1].x == vx[0].x) uinc = 0; \
	else uinc = ((vx[1].u - vx[0].u) << 16) / (vx[1].x - vx[0].x); \
	if (vx[1].y == vx[0].y) vinc = 0; \
	else vinc = ((vx[1].v - vx[0].v) << 16) / (vx[1].y - vx[0].y); \
 \
	for (y = vx[0].y, vpos = vx[0].v << 16; y < vx[1].y; y++, vpos+= vinc) { \
		v = vpos >> 16; \
		for (x = vx[0].x, upos = vx[0].u << 16; x < vx[1].x; x++, upos+= uinc) { \
			u = upos >> 16; \
			drawPixel(x, y, ftx); \
		} \
	}

void drawSpriteTDecal(Vertex * vx) {
	int x, y;
	int u, v;
	int upos, uinc;
	int vpos, vinc;

#ifdef GS_LOG
	GS_LOG("drawSpriteTDecal %dx%d - %dx%d %lx; tex: %dx%d - %dx%d (psm=%x, cbp=%x, csm=%x, cpsm=%x)\n",
		   vx[0].x, vx[0].y, vx[1].x, vx[1].y, gs.rgba,
		   vx[0].u, vx[0].v, vx[1].u, vx[1].v,
		   tex0->psm, tex0->cbp, tex0->csm, tex0->cpsm);
#endif

	_drawSpriteT(
		GetTexturePixel32(u, v)
	);
}

void drawSpriteTModulate(Vertex * vx) {
	int x, y;
	int u, v;
	int upos, uinc;
	int vpos, vinc;

#ifdef GS_LOG
	GS_LOG("drawSpriteTModulate %dx%d - %dx%d %lx; tex: %dx%d - %dx%d (psm=%x, cbp=%x, csm=%x, cpsm=%x)\n",
		   vx[0].x, vx[0].y, vx[1].x, vx[1].y, gs.rgba,
		   vx[0].u, vx[0].v, vx[1].u, vx[1].v,
		   tex0->psm, tex0->cbp, tex0->csm, tex0->cpsm);
#endif

	colSetCol(gs.rgba);
	_drawSpriteT(
		colModulate(GetTexturePixel32(u, v))
	);
}

void drawSpriteTHighlight(Vertex * vx) {
	int x, y;
	int u, v;
	int upos, uinc;
	int vpos, vinc;

#ifdef GS_LOG
	GS_LOG("drawSpriteTHighlight %dx%d - %dx%d %lx; tex: %dx%d - %dx%d (psm=%x, cbp=%x, csm=%x, cpsm=%x)\n",
		   vx[0].x, vx[0].y, vx[1].x, vx[1].y, gs.rgba,
		   vx[0].u, vx[0].v, vx[1].u, vx[1].v,
		   tex0->psm, tex0->cbp, tex0->csm, tex0->cpsm);
#endif

	colSetCol(gs.rgba);
	_drawSpriteT(
		colHighlight(GetTexturePixel32(u, v))
	);
}

void drawSpriteTHighlight2(Vertex * vx) {
	int x, y;
	int u, v;
	int upos, uinc;
	int vpos, vinc;

#ifdef GS_LOG
	GS_LOG("drawSpriteTHighlight2 %dx%d - %dx%d %lx; tex: %dx%d - %dx%d (psm=%x, cbp=%x, csm=%x, cpsm=%x)\n",
		   vx[0].x, vx[0].y, vx[1].x, vx[1].y, gs.rgba,
		   vx[0].u, vx[0].v, vx[1].u, vx[1].v,
		   tex0->psm, tex0->cbp, tex0->csm, tex0->cpsm);
#endif

	colSetCol(gs.rgba);
	_drawSpriteT(
		colHighlight2(GetTexturePixel32(u, v))
	);
}

////////////////////////////////////////////////////////////////////////
// TEXTURED SPRITE WITH Z-MAPPING
////////////////////////////////////////////////////////////////////////

#define _drawSpriteT_Z(ftx) \
	SetTexture(); \
	SETdrawPixelZ(); \
 \
	if (vx[1].x == vx[0].x) uinc = 0; \
	else uinc = ((vx[1].u - vx[0].u) << 16) / (vx[1].x - vx[0].x); \
	if (vx[1].y == vx[0].y) vinc = 0; \
	else vinc = ((vx[1].v - vx[0].v) << 16) / (vx[1].y - vx[0].y); \
 \
	for (y = vx[0].y, vpos = vx[0].v << 16; y < vx[1].y; y++, vpos+= vinc) { \
		v = vpos >> 16; \
		for (x = vx[0].x, upos = vx[0].u << 16; x < vx[1].x; x++, upos+= uinc) { \
			u = upos >> 16; \
			drawPixelZ(x, y, ftx, vx[1].z); \
		} \
	}

void drawSpriteTDecal_Z(Vertex * vx) {
	int x, y;
	int u, v;
	int upos, uinc;
	int vpos, vinc;

#ifdef GS_LOG
	GS_LOG("drawSpriteTDecal_Z %dx%d - %dx%d %lx; tex: %dx%d - %dx%d (psm=%x, cbp=%x, csm=%x, cpsm=%x)\n",
		   vx[0].x, vx[0].y, vx[1].x, vx[1].y, gs.rgba,
		   vx[0].u, vx[0].v, vx[1].u, vx[1].v,
		   tex0->psm, tex0->cbp, tex0->csm, tex0->cpsm);
#endif

	_drawSpriteT_Z(
		GetTexturePixel32(u, v)
	);
}

void drawSpriteTModulate_Z(Vertex * vx) {
	int x, y;
	int u, v;
	int upos, uinc;
	int vpos, vinc;

#ifdef GS_LOG
	GS_LOG("drawSpriteTModulate_Z %dx%d - %dx%d %lx; tex: %dx%d - %dx%d (psm=%x, tbp0=%x, cbp=%x, csm=%x, cpsm=%x)\n",
		   vx[0].x, vx[0].y, vx[1].x, vx[1].y, gs.rgba,
		   vx[0].u, vx[0].v, vx[1].u, vx[1].v,
		   tex0->psm, tex0->tbp0, tex0->cbp, tex0->csm, tex0->cpsm);
#endif

	colSetCol(gs.rgba);
	_drawSpriteT_Z(
		colModulate(GetTexturePixel32(u, v))
	);
}

void drawSpriteTHighlight_Z(Vertex * vx) {
	int x, y;
	int u, v;
	int upos, uinc;
	int vpos, vinc;

#ifdef GS_LOG
	GS_LOG("drawSpriteTHighlight_Z %dx%d - %dx%d %lx; tex: %dx%d - %dx%d (psm=%x, cbp=%x, csm=%x, cpsm=%x)\n",
		   vx[0].x, vx[0].y, vx[1].x, vx[1].y, gs.rgba,
		   vx[0].u, vx[0].v, vx[1].u, vx[1].v,
		   tex0->psm, tex0->cbp, tex0->csm, tex0->cpsm);
#endif

	colSetCol(gs.rgba);
	_drawSpriteT_Z(
		colHighlight(GetTexturePixel32(u, v))
	);
}

void drawSpriteTHighlight2_Z(Vertex * vx) {
	int x, y;
	int u, v;
	int upos, uinc;
	int vpos, vinc;

#ifdef GS_LOG
	GS_LOG("drawSpriteTHighlight2_Z %dx%d - %dx%d %lx; tex: %dx%d - %dx%d (psm=%x, cbp=%x, csm=%x, cpsm=%x)\n",
		   vx[0].x, vx[0].y, vx[1].x, vx[1].y, gs.rgba,
		   vx[0].u, vx[0].v, vx[1].u, vx[1].v,
		   tex0->psm, tex0->cbp, tex0->csm, tex0->cpsm);
#endif

	colSetCol(gs.rgba);
	_drawSpriteT_Z(
		colHighlight2(GetTexturePixel32(u, v))
	);
}



typedef struct {
	s32 X, U, V;
	s32 R, G, B, A;
	s64 Z;
} tagPolyEdge;

tagPolyEdge ForwardEdges[1024*8];
tagPolyEdge BackwardEdges[1024*8];

#define drawTriangle(scanFunc, drawFunc) { \
	int i; \
	int nYMin, nYMax; \
	int nPosMin, nPosMax; \
	int nPos1, nPos2; \
	int nY; \
 \
	nYMin = v[0].y; \
	nYMax = v[0].y; \
	nPosMin = 0; \
	nPosMax = 0; \
 \
	for (i=1; i<3; i++) { \
		if (v[i].y > nYMax) { \
			nYMax = v[i].y; \
			nPosMax = i; \
		} \
 \
		if (v[i].y < nYMin) { \
			nYMin = v[i].y; \
			nPosMin = i; \
		} \
	} \
 \
	if (nYMax == nYMin) \
		return; \
 \
	nPos1 = nPosMin; \
	nPos2 = nPos1; \
	while (nPos1 != nPosMax) { \
		nPos2 ++; \
		if (nPos2 == 3) \
			nPos2 = 0; \
 \
		scanFunc(ForwardEdges, &v[nPos1], &v[nPos2]); \
		nPos1 = nPos2; \
   } \
 \
	nPos1 = nPosMin; \
	nPos2 = nPos1; \
	while (nPos1 != nPosMax) { \
		nPos2 --; \
		if (nPos2 == -1) \
			nPos2 = 2; \
 \
		scanFunc(BackwardEdges, &v[nPos1], &v[nPos2]); \
		nPos1 = nPos2; \
	} \
 \
 	if (nYMin < 0) nYMin = 0; \
	for (nY = nYMin; nY < nYMax; nY++) { \
		if (BackwardEdges[nY].X < ForwardEdges[nY].X) { \
			drawFunc(nY, &BackwardEdges[nY], &ForwardEdges[nY]); \
		} else { \
			drawFunc(nY, &ForwardEdges[nY], &BackwardEdges[nY]); \
		} \
	} \
}


void scanEdgeF(tagPolyEdge* pastEdge, Vertex *v1, Vertex *v2) {
	int nX, nY;
	int nXInc;

	if (v1->y == v2->y)
		return;

	nX = v1->x << 16;
	nXInc = ((v2->x - v1->x) << 16) / (v2->y - v1->y);

	for (nY = v1->y; nY < 0; nY++) {
		nX += nXInc;
	}
	for (; nY < v2->y; nY++) {
		pastEdge[nY].X = nX >> 16; nX += nXInc;
	}
}

void drawHLineF(int nY, tagPolyEdge *stEdgeIni, tagPolyEdge *stEdgeEnd) {
	int nX;

	if (stEdgeEnd->X <= stEdgeIni->X) {
		return;
	}

	for (nX = stEdgeIni->X; nX <= stEdgeEnd->X; nX++) {
		drawPixel(nX, nY, gs.rgba);
	}
}

void drawTriangleF(Vertex * v) {
#ifdef GS_LOG
	GS_LOG("drawTriangleF %dx%d - %dx%d - %dx%d rgba=%x\n",
		   v[0].x, v[0].y, v[1].x, v[1].y, v[2].x, v[2].y, gs.rgba);
#endif

	SETdrawPixel();

	drawTriangle(scanEdgeF, drawHLineF);
}

void scanEdgeFZ(tagPolyEdge* pastEdge, Vertex *v1, Vertex *v2) {
	s32 nX, nY;
	s32 nXInc;
	s64 nZ;
	s64 nZInc;

	if (v1->y == v2->y)
		return;

	nX = v1->x << 16;
	nZ = (u64)v1->z << 16;

	nXInc = ((v2->x - v1->x) << 16) / (v2->y - v1->y);
	nZInc = ((s64)((s64)v2->z - v1->z) << 16) / (v2->y - v1->y);

	for (nY = v1->y; nY < 0; nY++) {
		nX += nXInc;
		nZ += nZInc;
	}
	for (; nY < v2->y; nY++) {
		pastEdge[nY].X = nX >> 16; nX += nXInc;
		pastEdge[nY].Z = nZ; nZ += nZInc;
	}
}

void drawHLineFZ(int nY, tagPolyEdge *stEdgeIni, tagPolyEdge *stEdgeEnd) {
	s32 nX;
	s64 nZ;
	s64 nZInc;

	if (stEdgeEnd->X <= stEdgeIni->X) {
		return;
	}

	nZ = stEdgeIni->Z;
	nZInc = (s64)((s64)stEdgeEnd->Z - stEdgeIni->Z) / (stEdgeEnd->X - stEdgeIni->X);

	for (nX = stEdgeIni->X; nX <= stEdgeEnd->X; nX++) {
		drawPixelZ(nX, nY, gs.rgba, (u32)(nZ >> 16));
		nZ += nZInc;
	}
}

void drawTriangleF_Z(Vertex * v) {
#ifdef GS_LOG
	GS_LOG("drawTriangleF_Z %dx%dx%lx - %dx%dx%lx - %dx%dx%lx rgba=%x\n",
		   v[0].x, v[0].y, v[0].z, v[1].x, v[1].y, v[1].z, v[2].x, v[2].y, v[2].z, gs.rgba);
#endif

	SETdrawPixelZ();

	drawTriangle(scanEdgeFZ, drawHLineFZ);
}


void scanEdgeG(tagPolyEdge* pastEdge, Vertex *v1, Vertex *v2) {
	s32 nX, nY, nR, nG, nB, nA;
	s32 nXInc, nRInc, nGInc, nBInc, nAInc;
	s32 r1, g1, b1, a1;
	s32 r2, g2, b2, a2;

	if (v1->y == v2->y)
		return;

	b1 = (v1->rgba & 0x000000FF);
	g1 = (v1->rgba & 0x0000FF00) >> 8;
	r1 = (v1->rgba & 0x00FF0000) >> 16;
	a1 = (v1->rgba & 0xFF000000) >> 24;
	b2 = (v2->rgba & 0x000000FF);
	g2 = (v2->rgba & 0x0000FF00) >> 8;
	r2 = (v2->rgba & 0x00FF0000) >> 16;
	a2 = (v2->rgba & 0xFF000000) >> 24;

	nX = v1->x << 16;
	nR =    r1 << 16;
	nG =    g1 << 16;
	nB =    b1 << 16;
	nA =    a1 << 16;
	nXInc = ((v2->x - v1->x) << 16) / (v2->y - v1->y);
	nRInc = ((   r2 -    r1) << 16) / (v2->y - v1->y);
	nGInc = ((   g2 -    g1) << 16) / (v2->y - v1->y);
	nBInc = ((   b2 -    b1) << 16) / (v2->y - v1->y);
	nAInc = ((   a2 -    a1) << 16) / (v2->y - v1->y);

	for (nY = v1->y; nY < 0; nY++) {
		nX += nXInc;
		nR += nRInc;
		nG += nGInc;
		nB += nBInc;
		nA += nAInc;
	}
	for (; nY < v2->y; nY++) {
		pastEdge[nY].X = nX >> 16; nX += nXInc;
		pastEdge[nY].R = nR; nR += nRInc;
		pastEdge[nY].G = nG; nG += nGInc;
		pastEdge[nY].B = nB; nB += nBInc;
		pastEdge[nY].A = nA; nA += nAInc;
	}
}

void drawHLineG(int nY, tagPolyEdge *stEdgeIni, tagPolyEdge *stEdgeEnd) {
	s32 nX, nR, nG, nB, nA;
	s32 nRInc, nGInc, nBInc, nAInc;

	if (stEdgeEnd->X <= stEdgeIni->X) {
		return;
	}

	nR = stEdgeIni->R;
	nG = stEdgeIni->G;
	nB = stEdgeIni->B;
	nA = stEdgeIni->A;
	nRInc = (stEdgeEnd->R - stEdgeIni->R) / (stEdgeEnd->X - stEdgeIni->X);
	nGInc = (stEdgeEnd->G - stEdgeIni->G) / (stEdgeEnd->X - stEdgeIni->X);
	nBInc = (stEdgeEnd->B - stEdgeIni->B) / (stEdgeEnd->X - stEdgeIni->X);
	nAInc = (stEdgeEnd->A - stEdgeIni->A) / (stEdgeEnd->X - stEdgeIni->X);

	for (nX = stEdgeIni->X; nX <= stEdgeEnd->X; nX++) {
		drawPixel(nX, nY, ( nR & 0xff0000)        | 
						  ((nG & 0xff0000) >> 8)  | 
						  ((nB & 0xff0000) >> 16) |
						  ((nA & 0xff0000) << 8));
		nR += nRInc; nG += nGInc; nB += nBInc; nA += nAInc;
	}
}

void drawTriangleG(Vertex * v) {
#ifdef GS_LOG
	GS_LOG("drawTriangleG %dx%d - %dx%d - %dx%d rgba=%x\n",
		   v[0].x, v[0].y, v[1].x, v[1].y, v[2].x, v[2].y, v[0].rgba);
#endif

	SETdrawPixel();

	drawTriangle(scanEdgeG, drawHLineG);
}


void scanEdgeGZ(tagPolyEdge* pastEdge, Vertex *v1, Vertex *v2) {
	s32 nX, nY, nR, nG, nB, nA;
	s32 nXInc, nRInc, nGInc, nBInc, nAInc;
	s32 r1, g1, b1, a1;
	s32 r2, g2, b2, a2;
	s64 nZ;
	s64 nZInc;

	if (v1->y == v2->y)
		return;

	b1 = (v1->rgba & 0x000000FF);
	g1 = (v1->rgba & 0x0000FF00) >> 8;
	r1 = (v1->rgba & 0x00FF0000) >> 16;
	a1 = (v1->rgba & 0xFF000000) >> 24;
	b2 = (v2->rgba & 0x000000FF);
	g2 = (v2->rgba & 0x0000FF00) >> 8;
	r2 = (v2->rgba & 0x00FF0000) >> 16;
	a2 = (v2->rgba & 0xFF000000) >> 24;

	nX = v1->x << 16;
	nZ = (u64)v1->z << 16;
	nR =    r1 << 16;
	nG =    g1 << 16;
	nB =    b1 << 16;
	nA =    a1 << 16;
	nXInc = ((v2->x - v1->x) << 16) / (v2->y - v1->y);
	nZInc = ((s64)((s64)v2->z - v1->z) << 16) / (v2->y - v1->y);
	nRInc = ((   r2 -    r1) << 16) / (v2->y - v1->y);
	nGInc = ((   g2 -    g1) << 16) / (v2->y - v1->y);
	nBInc = ((   b2 -    b1) << 16) / (v2->y - v1->y);
	nAInc = ((   a2 -    a1) << 16) / (v2->y - v1->y);

	for (nY = v1->y; nY < 0; nY++) {
		nX += nXInc;
		nZ += nZInc;
		nR += nRInc;
		nG += nGInc;
		nB += nBInc;
		nA += nAInc;
	}
	for (;nY < v2->y; nY++) {
		pastEdge[nY].X = nX >> 16; nX += nXInc;
		pastEdge[nY].Z = nZ; nZ += nZInc;
		pastEdge[nY].R = nR; nR += nRInc;
		pastEdge[nY].G = nG; nG += nGInc;
		pastEdge[nY].B = nB; nB += nBInc;
		pastEdge[nY].A = nA; nA += nAInc;
	}
}

void drawHLineGZ(int nY, tagPolyEdge *stEdgeIni, tagPolyEdge *stEdgeEnd) {
	s32 nX, nR, nG, nB, nA;
	s32 nRInc, nGInc, nBInc, nAInc;
	s64 nZ;
	s64 nZInc;

	if (stEdgeEnd->X <= stEdgeIni->X) {
		return;
	}

	nZ = stEdgeIni->Z;
	nR = stEdgeIni->R;
	nG = stEdgeIni->G;
	nB = stEdgeIni->B;
	nA = stEdgeIni->A;
	nZInc = (s64)((s64)stEdgeEnd->Z - stEdgeIni->Z) / (stEdgeEnd->X - stEdgeIni->X);
	nRInc = (stEdgeEnd->R - stEdgeIni->R) / (stEdgeEnd->X - stEdgeIni->X);
	nGInc = (stEdgeEnd->G - stEdgeIni->G) / (stEdgeEnd->X - stEdgeIni->X);
	nBInc = (stEdgeEnd->B - stEdgeIni->B) / (stEdgeEnd->X - stEdgeIni->X);
	nAInc = (stEdgeEnd->A - stEdgeIni->A) / (stEdgeEnd->X - stEdgeIni->X);

	for (nX = stEdgeIni->X; nX <= stEdgeEnd->X; nX++) {
		drawPixelZ(nX, nY, ( nR & 0xff0000)        | 
						   ((nG & 0xff0000) >> 8)  | 
						   ((nB & 0xff0000) >> 16) |
						   ((nA & 0xff0000) << 8), (u32)(nZ >> 16));
		nZ += nZInc;
		nR += nRInc; nG += nGInc; nB += nBInc; nA += nAInc;
	}
}

void drawTriangleG_Z(Vertex * v) {
#ifdef GS_LOG
	GS_LOG("drawTriangleG_Z %dx%d - %dx%d - %dx%d rgba=%x\n",
		   v[0].x, v[0].y, v[1].x, v[1].y, v[2].x, v[2].y, v[0].rgba);
#endif

	SETdrawPixelZ();

	drawTriangle(scanEdgeGZ, drawHLineGZ);
}


void scanEdgeFT(tagPolyEdge* pastEdge, Vertex *v1, Vertex *v2) {
	int nX, nY, nU, nV;
	int nXInc, nUInc, nVInc;

	if (v1->y == v2->y)
		return;

	nX = v1->x << 16;
	nU = v1->u << 16;
	nV = v1->v << 16;

	nXInc = ((v2->x - v1->x) << 16) / (v2->y - v1->y);
	nUInc = ((v2->u - v1->u) << 16) / (v2->y - v1->y);
	nVInc = ((v2->v - v1->v) << 16) / (v2->y - v1->y);

	for (nY = v1->y; nY < 0; nY++) {
		nX += nXInc;
		nU += nUInc;
		nV += nVInc;
	}
	for (; nY < v2->y; nY++) {
		pastEdge[nY].X = nX >> 16; nX += nXInc;
		pastEdge[nY].U = nU; nU += nUInc;
		pastEdge[nY].V = nV; nV += nVInc;
	}
}


#define _drawHLineFT(name, ftx) \
void drawHLineFT##name(int nY, tagPolyEdge *stEdgeIni, tagPolyEdge *stEdgeEnd) { \
	int nX, nU, nV; \
	int nUInc, nVInc; \
 \
	if (stEdgeEnd->X <= stEdgeIni->X) { \
		return; \
	} \
 \
	nX = stEdgeIni->X; \
	nU = stEdgeIni->U; \
	nV = stEdgeIni->V; \
	nUInc = (stEdgeEnd->U - stEdgeIni->U) / (stEdgeEnd->X - stEdgeIni->X); \
	nVInc = (stEdgeEnd->V - stEdgeIni->V) / (stEdgeEnd->X - stEdgeIni->X); \
 \
	for (nX = stEdgeIni->X; nX <= stEdgeEnd->X; nX++) { \
		drawPixel(nX, nY, ftx); \
		nU += nUInc; nV += nVInc; \
	} \
}

_drawHLineFT(Decal,
	GetTexturePixel32(nU >> 16, nV >> 16)
);
_drawHLineFT(Modulate,
	colModulate(GetTexturePixel32(nU >> 16, nV >> 16))
);
_drawHLineFT(Highlight,
	colHighlight(GetTexturePixel32(nU >> 16, nV >> 16))
);
_drawHLineFT(Highlight2,
	colHighlight2(GetTexturePixel32(nU >> 16, nV >> 16))
);

void drawTriangleFTDecal(Vertex * v) {
#ifdef GS_LOG
	GS_LOG("drawTriangleFTDecal %dx%d - %dx%d - %dx%d rgba=%x\n",
		   v[0].x, v[0].y, v[1].x, v[1].y, v[2].x, v[2].y, gs.rgba);
	GS_LOG("uv: %xx%x - %xx%x - %xx%x\n",
		   v[0].u, v[0].v, v[1].u, v[1].v, v[2].u, v[2].v);
#endif

	SetTexture();
	SETdrawPixel();

	drawTriangle(scanEdgeFT, drawHLineFTDecal);
}

void drawTriangleFTModulate(Vertex * v) {
#ifdef GS_LOG
	GS_LOG("drawTriangleFTModulate %dx%d - %dx%d - %dx%d rgba=%x\n",
		   v[0].x, v[0].y, v[1].x, v[1].y, v[2].x, v[2].y, gs.rgba);
	GS_LOG("uv: %xx%x - %xx%x - %xx%x\n",
		   v[0].u, v[0].v, v[1].u, v[1].v, v[2].u, v[2].v);
#endif

	SetTexture();
	SETdrawPixel();
	colSetCol(gs.rgba);

	drawTriangle(scanEdgeFT, drawHLineFTModulate);
}

void drawTriangleFTHighlight(Vertex * v) {
#ifdef GS_LOG
	GS_LOG("drawTriangleFTHighlight %dx%d - %dx%d - %dx%d rgba=%x\n",
		   v[0].x, v[0].y, v[1].x, v[1].y, v[2].x, v[2].y, gs.rgba);
	GS_LOG("uv: %xx%x - %xx%x - %xx%x\n",
		   v[0].u, v[0].v, v[1].u, v[1].v, v[2].u, v[2].v);
#endif

	SetTexture();
	SETdrawPixel();
	colSetCol(gs.rgba);

	drawTriangle(scanEdgeFT, drawHLineFTHighlight);
}

void drawTriangleFTHighlight2(Vertex * v) {
#ifdef GS_LOG
	GS_LOG("drawTriangleFTHighlight2 %dx%d - %dx%d - %dx%d rgba=%x\n",
		   v[0].x, v[0].y, v[1].x, v[1].y, v[2].x, v[2].y, gs.rgba);
	GS_LOG("uv: %xx%x - %xx%x - %xx%x\n",
		   v[0].u, v[0].v, v[1].u, v[1].v, v[2].u, v[2].v);
#endif

	SetTexture();
	SETdrawPixel();
	colSetCol(gs.rgba);

	drawTriangle(scanEdgeFT, drawHLineFTHighlight2);
}

void scanEdgeFTZ(tagPolyEdge* pastEdge, Vertex *v1, Vertex *v2) {
	int nX, nY, nU, nV;
	int nXInc, nUInc, nVInc;
	s64 nZ;
	s64 nZInc;

	if (v1->y == v2->y)
		return;

	nX = v1->x << 16;
	nZ = (u64)v1->z << 16;
	nU = v1->u << 16;
	nV = v1->v << 16;

	nXInc = ((v2->x - v1->x) << 16) / (v2->y - v1->y);
	nZInc = ((s64)((s64)v2->z - v1->z) << 16) / (v2->y - v1->y);
	nUInc = ((v2->u - v1->u) << 16) / (v2->y - v1->y);
	nVInc = ((v2->v - v1->v) << 16) / (v2->y - v1->y);

	for (nY = v1->y; nY < 0; nY++) {
		nX += nXInc;
		nZ += nZInc;
		nU += nUInc;
		nV += nVInc;
	}
	for (; nY < v2->y; nY++) {
		pastEdge[nY].X = nX >> 16; nX += nXInc;
		pastEdge[nY].Z = nZ; nZ += nZInc;
		pastEdge[nY].U = nU; nU += nUInc;
		pastEdge[nY].V = nV; nV += nVInc;
	}
}


#define _drawHLineFTZ(name, ftx) \
void drawHLineFTZ##name(int nY, tagPolyEdge *stEdgeIni, tagPolyEdge *stEdgeEnd) { \
	int nX, nU, nV; \
	int nUInc, nVInc; \
	s64 nZ; \
	s64 nZInc; \
 \
	if (stEdgeEnd->X <= stEdgeIni->X) { \
		return; \
	} \
 \
	nX = stEdgeIni->X; \
	nZ = stEdgeIni->Z; \
	nU = stEdgeIni->U; \
	nV = stEdgeIni->V; \
	nZInc = (s64)((s64)stEdgeEnd->Z - stEdgeIni->Z) / (stEdgeEnd->X - stEdgeIni->X); \
	nUInc = (stEdgeEnd->U - stEdgeIni->U) / (stEdgeEnd->X - stEdgeIni->X); \
	nVInc = (stEdgeEnd->V - stEdgeIni->V) / (stEdgeEnd->X - stEdgeIni->X); \
 \
	for (nX = stEdgeIni->X; nX <= stEdgeEnd->X; nX++) { \
		drawPixelZ(nX, nY, ftx, (u32)(nZ >> 16)); \
		nZ += nZInc; \
		nU += nUInc; nV += nVInc; \
	} \
}

_drawHLineFTZ(Decal,
	GetTexturePixel32(nU >> 16, nV >> 16)
);
_drawHLineFTZ(Modulate,
	colModulate(GetTexturePixel32(nU >> 16, nV >> 16))
);
_drawHLineFTZ(Highlight,
	colHighlight(GetTexturePixel32(nU >> 16, nV >> 16))
);
_drawHLineFTZ(Highlight2,
	colHighlight2(GetTexturePixel32(nU >> 16, nV >> 16))
);

void drawTriangleFTDecal_Z(Vertex * v) {
#ifdef GS_LOG
	GS_LOG("drawTriangleFTDecal_Z %dx%d - %dx%d - %dx%d rgba=%x\n",
		   v[0].x, v[0].y, v[1].x, v[1].y, v[2].x, v[2].y, gs.rgba);
	GS_LOG("uv: %xx%x - %xx%x - %xx%x\n",
		   v[0].u, v[0].v, v[1].u, v[1].v, v[2].u, v[2].v);
#endif

	SetTexture();
	SETdrawPixelZ();

	drawTriangle(scanEdgeFTZ, drawHLineFTZDecal);
}

void drawTriangleFTModulate_Z(Vertex * v) {
#ifdef GS_LOG
	GS_LOG("drawTriangleFTModulate_Z %dx%d - %dx%d - %dx%d rgba=%x\n",
		   v[0].x, v[0].y, v[1].x, v[1].y, v[2].x, v[2].y, gs.rgba);
	GS_LOG("uv: %xx%x - %xx%x - %xx%x\n",
		   v[0].u, v[0].v, v[1].u, v[1].v, v[2].u, v[2].v);
#endif

	SetTexture();
	SETdrawPixelZ();
	colSetCol(gs.rgba);

	drawTriangle(scanEdgeFTZ, drawHLineFTZModulate);
}

void drawTriangleFTHighlight_Z(Vertex * v) {
#ifdef GS_LOG
	GS_LOG("drawTriangleFTHighlight_Z %dx%d - %dx%d - %dx%d rgba=%x\n",
		   v[0].x, v[0].y, v[1].x, v[1].y, v[2].x, v[2].y, gs.rgba);
	GS_LOG("uv: %xx%x - %xx%x - %xx%x\n",
		   v[0].u, v[0].v, v[1].u, v[1].v, v[2].u, v[2].v);
#endif

	SetTexture();
	SETdrawPixelZ();
	colSetCol(gs.rgba);

	drawTriangle(scanEdgeFTZ, drawHLineFTZHighlight);
}

void drawTriangleFTHighlight2_Z(Vertex * v) {
#ifdef GS_LOG
	GS_LOG("drawTriangleFTHighlight2_Z %dx%d - %dx%d - %dx%d rgba=%x\n",
		   v[0].x, v[0].y, v[1].x, v[1].y, v[2].x, v[2].y, gs.rgba);
	GS_LOG("uv: %xx%x - %xx%x - %xx%x\n",
		   v[0].u, v[0].v, v[1].u, v[1].v, v[2].u, v[2].v);
#endif

	SetTexture();
	SETdrawPixelZ();
	colSetCol(gs.rgba);

	drawTriangle(scanEdgeFTZ, drawHLineFTZHighlight2);
}

/**************************/
/* Gouraud Shaded/Textued */

void scanEdgeGT(tagPolyEdge* pastEdge, Vertex *v1, Vertex *v2) {
	s32 nX, nU, nV, nY, nR, nG, nB, nA;
	s32 nXInc, nUInc, nVInc, nRInc, nGInc, nBInc, nAInc;
	s32 r1, g1, b1, a1;
	s32 r2, g2, b2, a2;

	if (v1->y == v2->y)
		return;

	b1 = (v1->rgba & 0x000000FF);
	g1 = (v1->rgba & 0x0000FF00) >> 8;
	r1 = (v1->rgba & 0x00FF0000) >> 16;
	a1 = (v1->rgba & 0xFF000000) >> 24;
	b2 = (v2->rgba & 0x000000FF);
	g2 = (v2->rgba & 0x0000FF00) >> 8;
	r2 = (v2->rgba & 0x00FF0000) >> 16;
	a2 = (v2->rgba & 0xFF000000) >> 24;

	nX = v1->x << 16;
	nR =    r1 << 16;
	nG =    g1 << 16;
	nB =    b1 << 16;
	nA =    a1 << 16;
	nU = v1->u << 16;
	nV = v1->v << 16;

	nXInc = ((v2->x - v1->x) << 16) / (v2->y - v1->y);
	nRInc = ((   r2 -    r1) << 16) / (v2->y - v1->y);
	nGInc = ((   g2 -    g1) << 16) / (v2->y - v1->y);
	nBInc = ((   b2 -    b1) << 16) / (v2->y - v1->y);
	nAInc = ((   a2 -    a1) << 16) / (v2->y - v1->y);
	nUInc = ((v2->u - v1->u) << 16) / (v2->y - v1->y);
	nVInc = ((v2->v - v1->v) << 16) / (v2->y - v1->y);

	for (nY = v1->y; nY < 0; nY++) {
		nX += nXInc;
		nU += nUInc;
		nV += nVInc;
		nR += nRInc;
		nG += nGInc;
		nB += nBInc;
		nA += nAInc;
	}
	for (; nY < v2->y; nY++) {
		pastEdge[nY].X = nX >> 16; nX += nXInc;
		pastEdge[nY].U = nU; nU += nUInc;
		pastEdge[nY].V = nV; nV += nVInc;
		pastEdge[nY].R = nR; nR += nRInc;
		pastEdge[nY].G = nG; nG += nGInc;
		pastEdge[nY].B = nB; nB += nBInc;
		pastEdge[nY].A = nA; nA += nAInc;
	}
}

#define _drawHLineGT(name, ftx) \
void drawHLineGT##name(int nY, tagPolyEdge *stEdgeIni, tagPolyEdge *stEdgeEnd) { \
	s32 nX, nU, nV, nR, nG, nB, nA; \
	s32 nUInc, nVInc, nRInc, nGInc, nBInc, nAInc; \
 \
	if (stEdgeEnd->X <= stEdgeIni->X) { \
		return; \
	} \
 \
	nR = stEdgeIni->R; \
	nG = stEdgeIni->G; \
	nB = stEdgeIni->B; \
	nA = stEdgeIni->A; \
	nU = stEdgeIni->U; \
	nV = stEdgeIni->V; \
	nRInc = (stEdgeEnd->R - stEdgeIni->R) / (stEdgeEnd->X - stEdgeIni->X); \
	nGInc = (stEdgeEnd->G - stEdgeIni->G) / (stEdgeEnd->X - stEdgeIni->X); \
	nBInc = (stEdgeEnd->B - stEdgeIni->B) / (stEdgeEnd->X - stEdgeIni->X); \
	nAInc = (stEdgeEnd->A - stEdgeIni->A) / (stEdgeEnd->X - stEdgeIni->X); \
	nUInc = (stEdgeEnd->U - stEdgeIni->U) / (stEdgeEnd->X - stEdgeIni->X); \
	nVInc = (stEdgeEnd->V - stEdgeIni->V) / (stEdgeEnd->X - stEdgeIni->X); \
 \
	for (nX = stEdgeIni->X; nX <= stEdgeEnd->X; nX++) { \
		colSetCol(( nR & 0xff0000)        | \
				  ((nG & 0xff0000) >> 8)  | \
				  ((nB & 0xff0000) >> 16) | \
				  ((nA & 0xff0000) << 8)); \
		drawPixel(nX, nY, ftx); \
		nU += nUInc; nV += nVInc; \
		nR += nRInc; nG += nGInc; nB += nBInc; nA += nAInc; \
	} \
}

_drawHLineGT(Modulate,
	colModulate(GetTexturePixel32(nU >> 16, nV >> 16))
);
_drawHLineGT(Highlight,
	colHighlight(GetTexturePixel32(nU >> 16, nV >> 16))
);
_drawHLineGT(Highlight2,
	colHighlight2(GetTexturePixel32(nU >> 16, nV >> 16))
);


void drawTriangleGTDecal(Vertex * v) {
#ifdef GS_LOG
	GS_LOG("drawTriangleGTDecal %dx%d - %dx%d - %dx%d rgba=%x\n",
		   v[0].x, v[0].y, v[1].x, v[1].y, v[2].x, v[2].y, gs.rgba);
	GS_LOG("uv: %xx%x - %xx%x - %xx%x\n",
		   v[0].u, v[0].v, v[1].u, v[1].v, v[2].u, v[2].v);
#endif

	drawTriangleFTDecal(v);
}

void drawTriangleGTModulate(Vertex * v) {
#ifdef GS_LOG
	GS_LOG("drawTriangleGTModulate %dx%d - %dx%d - %dx%d rgba=%x\n",
		   v[0].x, v[0].y, v[1].x, v[1].y, v[2].x, v[2].y, gs.rgba);
	GS_LOG("uv: %xx%x - %xx%x - %xx%x\n",
		   v[0].u, v[0].v, v[1].u, v[1].v, v[2].u, v[2].v);
#endif

	SetTexture();
	SETdrawPixel();

	drawTriangle(scanEdgeGT, drawHLineGTModulate);
}

void drawTriangleGTHighlight(Vertex * v) {
#ifdef GS_LOG
	GS_LOG("drawTriangleGTHighlight %dx%d - %dx%d - %dx%d rgba=%x\n",
		   v[0].x, v[0].y, v[1].x, v[1].y, v[2].x, v[2].y, gs.rgba);
	GS_LOG("uv: %xx%x - %xx%x - %xx%x\n",
		   v[0].u, v[0].v, v[1].u, v[1].v, v[2].u, v[2].v);
#endif

	SetTexture();
	SETdrawPixel();

	drawTriangle(scanEdgeGT, drawHLineGTHighlight);
}

void drawTriangleGTHighlight2(Vertex * v) {
#ifdef GS_LOG
	GS_LOG("drawTriangleGTHighlight2 %dx%d - %dx%d - %dx%d rgba=%x\n",
		   v[0].x, v[0].y, v[1].x, v[1].y, v[2].x, v[2].y, gs.rgba);
	GS_LOG("uv: %xx%x - %xx%x - %xx%x\n",
		   v[0].u, v[0].v, v[1].u, v[1].v, v[2].u, v[2].v);
#endif

	SetTexture();
	SETdrawPixel();

	drawTriangle(scanEdgeGT, drawHLineGTHighlight2);
}

void scanEdgeGTZ(tagPolyEdge* pastEdge, Vertex *v1, Vertex *v2) {
	s32 nX, nU, nV, nY, nR, nG, nB, nA;
	s32 nXInc, nUInc, nVInc, nRInc, nGInc, nBInc, nAInc;
	s64 nZ;
	s64 nZInc;
	s32 r1, g1, b1, a1;
	s32 r2, g2, b2, a2;

	if (v1->y == v2->y)
		return;

	b1 = (v1->rgba & 0x000000FF);
	g1 = (v1->rgba & 0x0000FF00) >> 8;
	r1 = (v1->rgba & 0x00FF0000) >> 16;
	a1 = (v1->rgba & 0xFF000000) >> 24;
	b2 = (v2->rgba & 0x000000FF);
	g2 = (v2->rgba & 0x0000FF00) >> 8;
	r2 = (v2->rgba & 0x00FF0000) >> 16;
	a2 = (v2->rgba & 0xFF000000) >> 24;

	nX = v1->x << 16;
	nZ = (u64)v1->z << 16;
	nR =    r1 << 16;
	nG =    g1 << 16;
	nB =    b1 << 16;
	nA =    a1 << 16;
	nU = v1->u << 16;
	nV = v1->v << 16;

	nXInc = ((v2->x - v1->x) << 16) / (v2->y - v1->y);
	nZInc = ((s64)((s64)v2->z - v1->z) << 16) / (v2->y - v1->y);
	nRInc = ((   r2 -    r1) << 16) / (v2->y - v1->y);
	nGInc = ((   g2 -    g1) << 16) / (v2->y - v1->y);
	nBInc = ((   b2 -    b1) << 16) / (v2->y - v1->y);
	nAInc = ((   a2 -    a1) << 16) / (v2->y - v1->y);
	nUInc = ((v2->u - v1->u) << 16) / (v2->y - v1->y);
	nVInc = ((v2->v - v1->v) << 16) / (v2->y - v1->y);

	for (nY = v1->y; nY < 0; nY++) {
		nX += nXInc;
		nZ += nZInc;
		nU += nUInc;
		nV += nVInc;
		nR += nRInc;
		nG += nGInc;
		nB += nBInc;
		nA += nAInc;
	}
	for (; nY < v2->y; nY++) {
		pastEdge[nY].X = nX >> 16; nX += nXInc;
		pastEdge[nY].Z = nZ; nZ += nZInc;
		pastEdge[nY].U = nU; nU += nUInc;
		pastEdge[nY].V = nV; nV += nVInc;
		pastEdge[nY].R = nR; nR += nRInc;
		pastEdge[nY].G = nG; nG += nGInc;
		pastEdge[nY].B = nB; nB += nBInc;
		pastEdge[nY].A = nA; nA += nAInc;
	}
}

#define _drawHLineGTZ(name, ftx) \
void drawHLineGTZ##name(int nY, tagPolyEdge *stEdgeIni, tagPolyEdge *stEdgeEnd) { \
	s32 nX, nU, nV, nR, nG, nB, nA; \
	s32 nUInc, nVInc, nRInc, nGInc, nBInc, nAInc; \
	s64 nZ; \
	s64 nZInc; \
 \
	if (stEdgeEnd->X <= stEdgeIni->X) { \
		return; \
	} \
 \
	nZ = stEdgeIni->Z; \
	nR = stEdgeIni->R; \
	nG = stEdgeIni->G; \
	nB = stEdgeIni->B; \
	nA = stEdgeIni->A; \
	nU = stEdgeIni->U; \
	nV = stEdgeIni->V; \
	nZInc = (s64)((s64)stEdgeEnd->Z - stEdgeIni->Z) / (stEdgeEnd->X - stEdgeIni->X); \
	nRInc = (stEdgeEnd->R - stEdgeIni->R) / (stEdgeEnd->X - stEdgeIni->X); \
	nGInc = (stEdgeEnd->G - stEdgeIni->G) / (stEdgeEnd->X - stEdgeIni->X); \
	nBInc = (stEdgeEnd->B - stEdgeIni->B) / (stEdgeEnd->X - stEdgeIni->X); \
	nAInc = (stEdgeEnd->A - stEdgeIni->A) / (stEdgeEnd->X - stEdgeIni->X); \
	nUInc = (stEdgeEnd->U - stEdgeIni->U) / (stEdgeEnd->X - stEdgeIni->X); \
	nVInc = (stEdgeEnd->V - stEdgeIni->V) / (stEdgeEnd->X - stEdgeIni->X); \
 \
	for (nX = stEdgeIni->X; nX <= stEdgeEnd->X; nX++) { \
		colSetCol(( nR & 0xff0000)        | \
				  ((nG & 0xff0000) >> 8)  | \
				  ((nB & 0xff0000) >> 16) | \
				  ((nA & 0xff0000) << 8)); \
		drawPixelZ(nX, nY, ftx, (u32)(nZ >> 16)); \
		nZ += nZInc; \
		nU += nUInc; nV += nVInc; \
		nR += nRInc; nG += nGInc; nB += nBInc; nA += nAInc; \
	} \
}

_drawHLineGTZ(Modulate,
	colModulate(GetTexturePixel32(nU >> 16, nV >> 16))
);
_drawHLineGTZ(Highlight,
	colHighlight(GetTexturePixel32(nU >> 16, nV >> 16))
);
_drawHLineGTZ(Highlight2,
	colHighlight2(GetTexturePixel32(nU >> 16, nV >> 16))
);

void drawTriangleGTDecal_Z(Vertex * v) {
#ifdef GS_LOG
	GS_LOG("drawTriangleGTDecal_Z %dx%d - %dx%d - %dx%d rgba=%x\n",
		   v[0].x, v[0].y, v[1].x, v[1].y, v[2].x, v[2].y, gs.rgba);
	GS_LOG("uv: %xx%x - %xx%x - %xx%x\n",
		   v[0].u, v[0].v, v[1].u, v[1].v, v[2].u, v[2].v);
#endif

	drawTriangleFTDecal_Z(v);
}

void drawTriangleGTModulate_Z(Vertex * v) {
#ifdef GS_LOG
	GS_LOG("drawTriangleGTModulate_Z %dx%d - %dx%d - %dx%d rgba=%x\n",
		   v[0].x, v[0].y, v[1].x, v[1].y, v[2].x, v[2].y, gs.rgba);
	GS_LOG("uv: %xx%x - %xx%x - %xx%x\n",
		   v[0].u, v[0].v, v[1].u, v[1].v, v[2].u, v[2].v);
#endif

	SetTexture();
	SETdrawPixelZ();

	drawTriangle(scanEdgeGTZ, drawHLineGTZModulate);
}

void drawTriangleGTHighlight_Z(Vertex * v) {
#ifdef GS_LOG
	GS_LOG("drawTriangleGTHighlight_Z %dx%d - %dx%d - %dx%d rgba=%x\n",
		   v[0].x, v[0].y, v[1].x, v[1].y, v[2].x, v[2].y, gs.rgba);
	GS_LOG("uv: %xx%x - %xx%x - %xx%x\n",
		   v[0].u, v[0].v, v[1].u, v[1].v, v[2].u, v[2].v);
#endif

	SetTexture();
	SETdrawPixelZ();

	drawTriangle(scanEdgeGTZ, drawHLineGTZHighlight);
}

void drawTriangleGTHighlight2_Z(Vertex * v) {
#ifdef GS_LOG
	GS_LOG("drawTriangleGTHighlight2_Z %dx%d - %dx%d - %dx%d rgba=%x\n",
		   v[0].x, v[0].y, v[1].x, v[1].y, v[2].x, v[2].y, gs.rgba);
	GS_LOG("uv: %xx%x - %xx%x - %xx%x\n",
		   v[0].u, v[0].v, v[1].u, v[1].v, v[2].u, v[2].v);
#endif

	SetTexture();
	SETdrawPixelZ();

	drawTriangle(scanEdgeGTZ, drawHLineGTZHighlight2);
}


/*
 3DFC
 http://www.geocities.com/SiliconValley/Bay/1704
 
*/

