function pipeline()
{
	this.world = new matrix();
	this.view = new matrix();
	this.wv_cat = new matrix();
	this.projection = new matrix();
	this.stream = new Array();
	this.light = new light();
	this.texture = null;
	this.shininess=0;
}

pipeline.prototype.clear = function()
{
	backcontext.fillStyle = "rgb(0, 0, 0)";
    backcontext.fillRect(0, 0, backcanvas.width, backcanvas.height);
    frameBuffer = backcontext.getImageData(0, 0, backcanvas.width, backcanvas.height);
}

pipeline.prototype.present = function()
{
	backcontext.putImageData(frameBuffer, 0, 0);
	context.drawImage(backcanvas, 0, 0, canvas.width, canvas.height);
	this.clear();
}

pipeline.prototype.draw = function()
{
	this.wv_cat = mat_mul(this.view, this.world);

	for(var i = 0; i+3 <= this.stream.length; )
	{
		tri = new Array();
		tri[0] = this.stream[i++].clone();
		tri[1] = this.stream[i++].clone();
		tri[2] = this.stream[i++].clone();
		
		this.TRANSFORM_LIGHT(tri);
		this.NORMALIZE_CLIP(tri);
		this.TARGETMAP_RASTERIZE(tri);
	}
}

pipeline.prototype.loadtexture = function(imageURL)
{
	var img = new Image();
	img.onload = function()
	{
		var cnvs        = document.createElement('canvas');
		cnvs.width      = img.width;
		cnvs.height     = img.height;

		var ctxt = cnvs.getContext("2d");
		ctxt.drawImage(img, 0, 0);

		pipe.texture = ctxt.getImageData(0, 0, img.width, img.height);
	};
	img.src = imageURL;
	
}

pipeline.prototype.TRANSFORM_LIGHT = function(tri)
{
	var cm = new vector(-this.view.m_14, -this.view.m_24, -this.view.m_34);
	var lt = this.light.clone();

	this.view.vec_mul_eq(lt.pos);

	for(var i = 0; i < 3; ++i)
	{
		this.wv_cat.vec_mul_eq(tri[i].pos);
		this.wv_cat.nrm_mul_eq(tri[i].nrm);

		var neg_pos = tri[i].pos.negate();
		var lv = vec_add(lt.pos, neg_pos);
		var cv = vec_add(cm, neg_pos);
		lv.normalize();
		cv.normalize();

		var dp = Math.max(0, dot_prod(lv, tri[i].nrm));

		tri[i].diff.mul_eq( col_add(lt.ambt, lt.diff.clone().scale(dp)) );
		tri[i].diff.clamp(1.0);

		var hv = vec_add(lv, cv);
		hv.normalize();
		dp = Math.max(0, dot_prod(hv, tri[i].nrm));
		dp = dp / (this.shininess - (this.shininess * dp) + dp);

		tri[i].spec.mul_eq(lt.spec.clone().scale(dp));
		tri[i].spec.clamp(1.0);
	}
}

pipeline.prototype.NORMALIZE_CLIP = function(tri)
{
	for(var i = 0; i < 3; ++i)
	{
		this.projection.vec_mul_eq(tri[i].pos);
		tri[i].pos.z = 1.0 / tri[i].pos.z;
		tri[i].pos.x *= tri[i].pos.z;
		tri[i].pos.y *= tri[i].pos.z;
	}
}

function COUNTER_CLOCKWISE(v)
{
	if((v[1].pos.x - v[0].pos.x)*(v[2].pos.y - v[1].pos.y) +
		(v[1].pos.y - v[0].pos.y)*(v[1].pos.x - v[2].pos.x) < 0)
		return true;
	else
		return false;
}

pipeline.prototype.TARGETMAP_RASTERIZE = function(tri)
{
	var hWidth = frameBuffer.width / 2.0;
	var hHeight = frameBuffer.height / 2.0;
	var scl = ScaleMatrix(new vector(hWidth, -hHeight, -1));
	var trn = TranslationMatrix(new vector(hWidth, hHeight, 0));
	trn.mul_eq(scl);

	for(var i = 0; i < 3; ++i)
	{
		trn.vec_mul_eq(tri[i].pos);
	}

	if(COUNTER_CLOCKWISE(tri))
		TRIANGLE_DRAW(tri);
}
