function matrix()
{
	this.m_11 = 0.0;
	this.m_12 = 0.0;
	this.m_13 = 0.0;
	this.m_14 = 0.0;
	this.m_21 = 0.0;
	this.m_22 = 0.0;
	this.m_23 = 0.0;
	this.m_24 = 0.0;
	this.m_31 = 0.0;
	this.m_32 = 0.0;
	this.m_33 = 0.0;
	this.m_34 = 0.0;
	this.m_41 = 0.0;
	this.m_42 = 0.0;
	this.m_43 = 0.0;
	this.m_44 = 0.0;
}   

matrix.prototype.init = function(	im_11, im_12, im_13, im_14,
									im_21, im_22, im_23, im_24,
									im_31, im_32, im_33, im_34,
									im_41, im_42, im_43, im_44 )
{
	this.m_11 = im_11;
	this.m_12 = im_12;
	this.m_13 = im_13;
	this.m_14 = im_14;
	this.m_21 = im_21;
	this.m_22 = im_22;
	this.m_23 = im_23;
	this.m_24 = im_24;
	this.m_31 = im_31;
	this.m_32 = im_32;
	this.m_33 = im_33;
	this.m_34 = im_34;
	this.m_41 = im_41;
	this.m_42 = im_42;
	this.m_43 = im_43;
	this.m_44 = im_44;
}   

matrix.prototype.eq = function(in_m)
{
	this.m_11 = in_m.m_11;
	this.m_12 = in_m.m_12;
	this.m_13 = in_m.m_13;
	this.m_14 = in_m.m_14;
	this.m_21 = in_m.m_21;
	this.m_22 = in_m.m_22;
	this.m_23 = in_m.m_23;
	this.m_24 = in_m.m_24;
	this.m_31 = in_m.m_31;
	this.m_32 = in_m.m_32;
	this.m_33 = in_m.m_33;
	this.m_34 = in_m.m_34;
	this.m_41 = in_m.m_41;
	this.m_42 = in_m.m_42;
	this.m_43 = in_m.m_43;
	this.m_44 = in_m.m_44;

	return this;
}

matrix.prototype.clone = function()
{
	var m_out = new matrix();
	m_out.eq(this);	

	return m_out;
}

matrix.prototype.mul_eq = function(rm)
{
	var m1;
	var m2;
	var m3;
	var m4;
	
	var m1 = this.m_11*rm.m_11 + this.m_12*rm.m_21 + this.m_13*rm.m_31 + this.m_14*rm.m_41; 
	var m2 = this.m_11*rm.m_12 + this.m_12*rm.m_22 + this.m_13*rm.m_32 + this.m_14*rm.m_42; 
	var m3 = this.m_11*rm.m_13 + this.m_12*rm.m_23 + this.m_13*rm.m_33 + this.m_14*rm.m_43; 
	var m4 = this.m_11*rm.m_14 + this.m_12*rm.m_24 + this.m_13*rm.m_34 + this.m_14*rm.m_44; 
	this.m_11 = m1; this.m_12 = m2; this.m_13 = m3; this.m_14 = m4;
	m1 = this.m_21*rm.m_11 + this.m_22*rm.m_21 + this.m_23*rm.m_31 + this.m_24*rm.m_41;
	m2 = this.m_21*rm.m_12 + this.m_22*rm.m_22 + this.m_23*rm.m_32 + this.m_24*rm.m_42;
	m3 = this.m_21*rm.m_13 + this.m_22*rm.m_23 + this.m_23*rm.m_33 + this.m_24*rm.m_43;
	m4 = this.m_21*rm.m_14 + this.m_22*rm.m_24 + this.m_23*rm.m_34 + this.m_24*rm.m_44;
	this.m_21 = m1; this.m_22 = m2; this.m_23 = m3; this.m_24 = m4;
	m1 = this.m_31*rm.m_11 + this.m_32*rm.m_21 + this.m_33*rm.m_31 + this.m_34*rm.m_41;
	m2 = this.m_31*rm.m_12 + this.m_32*rm.m_22 + this.m_33*rm.m_32 + this.m_34*rm.m_42;
	m3 = this.m_31*rm.m_13 + this.m_32*rm.m_23 + this.m_33*rm.m_33 + this.m_34*rm.m_43;
	m4 = this.m_31*rm.m_14 + this.m_32*rm.m_24 + this.m_33*rm.m_34 + this.m_34*rm.m_44;
	this.m_31 = m1; this.m_32 = m2; this.m_33 = m3; this.m_34 = m4;
	m1 = this.m_41*rm.m_11 + this.m_42*rm.m_21 + this.m_43*rm.m_31 + this.m_44*rm.m_41;
	m2 = this.m_41*rm.m_12 + this.m_42*rm.m_22 + this.m_43*rm.m_32 + this.m_44*rm.m_42;
	m3 = this.m_41*rm.m_13 + this.m_42*rm.m_23 + this.m_43*rm.m_33 + this.m_44*rm.m_43;
	m4 = this.m_41*rm.m_14 + this.m_42*rm.m_24 + this.m_43*rm.m_34 + this.m_44*rm.m_44;
	this.m_41 = m1; this.m_42 = m2; this.m_43 = m3; this.m_44 = m4;

	return this;
}

matrix.prototype.vec_mul_eq = function(rv)
{
	var tmp = rv.clone();

	rv.x = tmp.x * this.m_11 + tmp.y * this.m_12 + tmp.z * this.m_13 + this.m_14;
	rv.y = tmp.x * this.m_21 + tmp.y * this.m_22 + tmp.z * this.m_23 + this.m_24;
	rv.z = tmp.x * this.m_31 + tmp.y * this.m_32 + tmp.z * this.m_33 + this.m_34;
	                     
	return rv;
}

matrix.prototype.nrm_mul_eq = function(rv)
{
	var tmp = rv.clone();
	
	rv.x = tmp.x * this.m_11 + tmp.y * this.m_12 + tmp.z * this.m_13;
	rv.y = tmp.x * this.m_21 + tmp.y * this.m_22 + tmp.z * this.m_23;
	rv.z = tmp.x * this.m_31 + tmp.y * this.m_32 + tmp.z * this.m_33;
	
	return rv;
}

function mat_mul(rm1, rm2)
{
	return rm1.clone().mul_eq(rm2);
}

function ScaleMatrix(rv)
{
	var rm = new matrix();
	
	rm.init(rv.x, 0.0, 0.0, 0.0,
			0.0, rv.y, 0.0, 0.0,
			0.0, 0.0, rv.z, 0.0,
			0.0, 0.0, 0.0, 1.0 );

	return rm;
}

function TranslationMatrix(rv)
{
	var rm = new matrix();
	
	rm.init(1.0, 0.0, 0.0, rv.x,
			0.0, 1.0, 0.0, rv.y,
			0.0, 0.0, 1.0, rv.z,
			0.0, 0.0, 0.0, 1.0 );

	return rm;
}

function AxisRotationMatrix(rv, angle)
{
	var rm = new matrix();
	
	var cost = Math.cos(angle);
	var mcos = 1.0 - cost;
	var sint = Math.sin(angle);

	rm.m_11=cost+mcos*rv.x*rv.x; rm.m_12=mcos*rv.x*rv.y-sint*rv.z; rm.m_13=mcos*rv.x*rv.z+sint*rv.y; rm.m_14=0.0;
	rm.m_21=mcos*rv.x*rv.y+sint*rv.z; rm.m_22=cost+mcos*rv.y*rv.y; rm.m_23=mcos*rv.y*rv.z-sint*rv.x; rm.m_24=0.0;
	rm.m_31=mcos*rv.x*rv.z-sint*rv.y; rm.m_32=mcos*rv.y*rv.z+sint*rv.x; rm.m_33=cost+mcos*rv.z*rv.z; rm.m_34=0.0;
	rm.m_41= 0.0; rm.m_42= 0.0; rm.m_43= 0.0; rm.m_44=1.0;
	
	return rm;
}

//Build a matrix to rotate into the set of basis vectors i, j, k.
function BasisRotationMatrix(i, j, k)
{
	var rm = new matrix();
	
	var it = i.clone().normalize();
	var jt = j.clone().normalize();
	var kt = k.clone().normalize();
	
	rm.m_11=it.x;  rm.m_12=it.y;  rm.m_13=it.z;  rm.m_14=0.0;
	rm.m_21=jt.x;  rm.m_22=jt.y;  rm.m_23=jt.z;  rm.m_24=0.0;
	rm.m_31=kt.x;  rm.m_32=kt.y;  rm.m_33=kt.z;  rm.m_34=0.0;
	rm.m_41= 0.0;  rm.m_42= 0.0;  rm.m_43= 0.0;  rm.m_44=1.0;
	
	return rm;
}

function LookRotationMatrix(rvView, rvUp)
{
	var i = cross_prod(rvView, rvUp);
	return BasisRotationMatrix(i, rvView, cross_prod(i, rvView));
}

function HalfAngleCot(angle)
{
	return 1.0 / Math.tan(angle / 2.0);
}

//Build a perspective matrix based off the following parameters:
//hFov	- Horizontal field of view (Common value: PI / 2)
//a		- Aspect Ratio, Viewport width / height.
function ProjectionMatrix(hFov, a)
{
	var rm = new matrix();

	var t = HalfAngleCot(hFov);
	rm.m_11=t; rm.m_12=0.0; rm.m_13=0.0; rm.m_14=0.0;
	rm.m_21=0.0; rm.m_22=a*t; rm.m_23=0.0; rm.m_24=0.0;
	rm.m_31=0.0; rm.m_32=0.0; rm.m_33=1.0; rm.m_34=0.0;
	rm.m_41=0.0; rm.m_42=1.0; rm.m_43=0.0; rm.m_44=0.0;
	return rm;
}
