function SET_PIXEL(sx, sy, sr, sg, sb)
{
	if(sx < 0 || sy < 0 || sx >= frameBuffer.width || sy >= frameBuffer.height)
	{
		return;
	}

	var si = (sy*frameBuffer.width + sx)*4;
		
	frameBuffer.data[si++] = sr;	//red
	frameBuffer.data[si++] = sg;	//green
	frameBuffer.data[si++] = sb;	//blue
	frameBuffer.data[si] = 255;	//alpha
}

function SAMPLE_TEXTURE(iu, iv)
{
	if(pipe.texture == null)
		return new color(1.0, 1.0, 1.0);

	iu = Math.round(iu);
	iv = Math.round(iv);

	if(iu < 0)
		iu = 0;
	else if(iu >= pipe.texture.width)
		iu = pipe.texture.width - 1;
	if(iv < 0)
		iv = 0;
	else if(iv >= pipe.texture.height)
		iv = pipe.texture.height - 1;
	
	var ii = (iv*pipe.texture.width + iu)*4;
	
	var ir = pipe.texture.data[ii++] / 255;
	var ig = pipe.texture.data[ii++] / 255;
	var ib = pipe.texture.data[ii] / 255;
	
	return new color(ir, ig, ib);
}

function LINE_DRAW(lv0, lv1)
{
	var length;
	var dx;
	var dy;
	
	var lx = lv0.pos.x;
	var ly = lv0.pos.y;
	
	if((lv1.pos.x - lv0.pos.x) > 0)
		dx = lv1.pos.x - lv0.pos.x;
	else
		dx = lv0.pos.x - lv1.pos.x;
	
	if((lv1.pos.y - lv0.pos.y) > 0)
		dy = lv1.pos.y - lv0.pos.y;
	else
		dy = lv0.pos.y - lv1.pos.y;

	if(dy > dx)
		length = Math.round(dy);
	else
		length = Math.round(dx);
	
	var xstep = (lv1.pos.x - lv0.pos.x) / length;
	var ystep = (lv1.pos.y - lv0.pos.y) / length;

	for(var li = 0; li < length; ++li)
	{
		var l_col = color_lerp(lv0.diff, lv1.diff, li / (length-1));
		SET_PIXEL(Math.round(lx), Math.round(ly), l_col.r, l_col.g, l_col.b);
		lx += xstep;
		ly += ystep;
	}
}


function SCANLINE_DRAW(y, vBeg, vEnd)
{
	var x = Math.ceil(vBeg.pos.x);
	var xEnd = Math.ceil(vEnd.pos.x) - 1;
	var width = (vEnd.pos.x - vBeg.pos.x);
	
	//Setup Interpolants 
	var u = vBeg.tex.u;
	var v = vBeg.tex.v;
	var mu = (vEnd.tex.u - u) / width;
	var mv = (vEnd.tex.v - v) / width;
	var diff = vBeg.diff.clone().scale(256.0);
	var spec = vBeg.spec.clone().scale(256.0);
	var mDiff = col_add(vEnd.diff.clone().scale(256.0), diff.negate());
	mDiff.scale(1.0 / width);
	var mSpec = col_add(vEnd.spec.clone().scale(256.0), spec.negate())
	mSpec.scale(1.0 / width);
	var z = vBeg.pos.z;
	var mz = (vEnd.pos.z - z) / width;

	//Ensure sub-pixel accuracy
	var initDiff = x - vBeg.pos.x;
	u += initDiff * mu;
	v += initDiff * mv;
	var tmp = mDiff.clone();
	tmp.scale(initDiff);
	diff.add_eq(tmp);
	tmp = mSpec.clone();
	tmp.scale(initDiff);
	spec.add_eq(tmp);
	z += initDiff * mz;
	

	//Draw Scanline
	while(x <= xEnd)
	{
		var hz = 1.0 / z;
		var hu = u * hz;
		var hv = v * hz;
		var tCol = SAMPLE_TEXTURE(hu, hv);

		result = col_add(tCol.mul_eq(diff), spec);
		result.clamp(255.0);
		SET_PIXEL(x, y, Math.round(result.r), Math.round(result.g), Math.round(result.b));
		
		u += mu;
		v += mv;
		diff.add_eq(mDiff);
		spec.add_eq(mSpec);
		z += mz;
		++x;
	}
}

function TRIANGLE_DRAW(vtx)
{
	var t_width = 256.0;
	var t_height = 256.0;
	var Left = vtx[0].clone();
	var Right = vtx[0].clone();
	var mMid = vtx[0].clone();
	var mBot = vtx[0].clone();

	if(pipe.texture != null)
	{
		t_width = pipe.texture.width;
		t_height = pipe.texture.height;
	}

	//Order Vertex Indices
	var Top = (vtx[0].pos.y < vtx[1].pos.y) ? 0 : 1;
	Top = (vtx[Top].pos.y < vtx[2].pos.y) ? Top : 2;
	var Bot = (vtx[0].pos.y > vtx[1].pos.y) ? 0 : 1;
	Bot = (vtx[Bot].pos.y > vtx[2].pos.y) ? Bot : 2;
	var Mid = 3 - (Top + Bot);
	/////////////////////////////////
	
	
	//Setup Left/Right Vertex Indices
	var Lft;
	var Rht;
	var ml;
	var mr;
	var mp;
	if(((vtx[Top].pos.y - vtx[Mid].pos.y)*(vtx[Bot].pos.x - vtx[Top].pos.x) +
		(vtx[Mid].pos.x - vtx[Top].pos.x)*(vtx[Bot].pos.y - vtx[Top].pos.y)) < 0)
	{
		Lft = Mid;
		Rht = Bot;
		ml = mMid;
		mr = mBot;
		mp = Left;
	}
	else
	{
		Lft = Bot;
		Rht = Mid;
		ml = mBot;
		mr = mMid;
		mp = Right;
	}
	/////////////////////////////////
	
	//Setup Interpolation Values
	var y = Math.ceil(vtx[Top].pos.y);
	var yMid = Math.ceil(vtx[Mid].pos.y) - 1;
	var yEnd = Math.ceil(vtx[Bot].pos.y) - 1;
	var midHeight = vtx[Mid].pos.y - vtx[Top].pos.y;
	var height = vtx[Bot].pos.y - vtx[Top].pos.y;

	mMid.pos.x = (vtx[Mid].pos.x - vtx[Top].pos.x) / midHeight;
	mMid.pos.z = (vtx[Mid].pos.z - vtx[Top].pos.z) / midHeight;
	mBot.pos.x = (vtx[Bot].pos.x - vtx[Top].pos.x) / height;
	mBot.pos.z = (vtx[Bot].pos.z - vtx[Top].pos.z) / height;

	mMid.tex.u = t_width * (vtx[Mid].tex.u * vtx[Mid].pos.z - vtx[Top].tex.u * vtx[Top].pos.z) / midHeight;
	mMid.tex.v = t_height * (vtx[Mid].tex.v * vtx[Mid].pos.z - vtx[Top].tex.v * vtx[Top].pos.z) / midHeight;
	mMid.diff = col_add(vtx[Mid].diff, vtx[Top].diff.negate());
	mMid.diff.scale(1 / midHeight);
	mMid.spec = col_add(vtx[Mid].spec, vtx[Top].spec.negate());
	mMid.spec.scale(1 / midHeight);
	
	mBot.tex.u = t_width * (vtx[Bot].tex.u * vtx[Bot].pos.z - vtx[Top].tex.u * vtx[Top].pos.z) / height;
	mBot.tex.v = t_height * (vtx[Bot].tex.v * vtx[Bot].pos.z - vtx[Top].tex.v * vtx[Top].pos.z) / height;
	mBot.diff = col_add(vtx[Bot].diff, vtx[Top].diff.negate());
	mBot.diff.scale(1 / height);
	mBot.spec = col_add(vtx[Bot].spec, vtx[Top].spec.negate());
	mBot.spec.scale(1 / height);
	/////////////////////////////////
	
	//Initialize vertices
	var initDiff = y - vtx[Top].pos.y;
	Left.pos.x = vtx[Top].pos.x + (ml.pos.x * initDiff);
	Left.pos.z = vtx[Top].pos.z + (ml.pos.z * initDiff);
	var tmp = ml.diff.clone();
	tmp.scale(initDiff);
	Left.diff = col_add(vtx[Top].diff, tmp);
	tmp = ml.spec.clone();
	tmp.scale(initDiff);
	Left.spec = col_add(vtx[Top].spec, tmp);
	Left.tex.u = vtx[Top].pos.z * vtx[Top].tex.u * t_width + (ml.tex.u * initDiff);
	Left.tex.v = vtx[Top].pos.z * vtx[Top].tex.v * t_height + (ml.tex.v * initDiff);

	Right.pos.x = vtx[Top].pos.x + (mr.pos.x * initDiff);
	Right.pos.z = vtx[Top].pos.z + (mr.pos.z * initDiff);
	tmp = mr.diff.clone();
	tmp.scale(initDiff);
	Right.diff = col_add(vtx[Top].diff, tmp);
	tmp = mr.spec.clone();
	tmp.scale(initDiff);
	Right.spec = col_add(vtx[Top].spec, tmp);
	Right.tex.u = vtx[Top].pos.z * vtx[Top].tex.u * t_width + (mr.tex.u * initDiff);
	Right.tex.v = vtx[Top].pos.z * vtx[Top].tex.v * t_height + (mr.tex.v * initDiff);
	/////////////////////////////////
	
	var yStop = yMid;
	for(var i = 0; i < 2; ++i)
	{
		//Draw Triangle Half
		while(y <= yStop)
		{
			SCANLINE_DRAW(y, Left, Right);

			Left.diff.add_eq(ml.diff);
			Left.spec.add_eq(ml.spec);
			Left.pos.x += ml.pos.x;
			Left.pos.z += ml.pos.z;
			Left.tex.u += ml.tex.u;
			Left.tex.v += ml.tex.v;

			Right.diff.add_eq(mr.diff);
			Right.spec.add_eq(mr.spec);
			Right.pos.x += mr.pos.x;
			Right.pos.z += mr.pos.z;
			Right.tex.u += mr.tex.u;
			Right.tex.v += mr.tex.v;

			++y;
		}
		/////////////////////////////////

		if(i == 0)
		{
			yStop = yEnd;

			//Reset Interpolation Values
			height -= midHeight;

			mMid.pos.x = (vtx[Bot].pos.x - vtx[Mid].pos.x) / height;
			mMid.pos.z = (vtx[Bot].pos.z - vtx[Mid].pos.z) / height;
			mMid.tex.u = t_width * (vtx[Bot].tex.u * vtx[Bot].pos.z - vtx[Mid].tex.u * vtx[Mid].pos.z) / height;
			mMid.tex.v = t_height * (vtx[Bot].tex.v * vtx[Bot].pos.z - vtx[Mid].tex.v * vtx[Mid].pos.z) / height;
			mMid.diff = col_add(vtx[Bot].diff, vtx[Mid].diff.negate());
			mMid.diff.scale(1 / height);
			mMid.spec = col_add(vtx[Bot].spec, vtx[Mid].spec.negate());
			mMid.spec.scale(1 / height);
			/////////////////////////////////

			//Reset Middle Vertex
			initDiff = y - vtx[Mid].pos.y;
			mp.pos.x = vtx[Mid].pos.x + (initDiff * mMid.pos.x);
			mp.pos.z = vtx[Mid].pos.z + (initDiff * mMid.pos.z);
			tmp = mMid.diff.clone();
			tmp.scale(initDiff);
			mp.diff = col_add(vtx[Mid].diff, tmp);
			tmp = mMid.spec.clone();
			tmp.scale(initDiff);
			mp.spec = col_add(vtx[Mid].spec, tmp);
			mp.tex.u = vtx[Mid].pos.z * vtx[Mid].tex.u * t_width + (initDiff * mMid.tex.u);
			mp.tex.v = vtx[Mid].pos.z * vtx[Mid].tex.v * t_height + (initDiff * mMid.tex.v);
			/////////////////////////////////
		}
	}
}
