function TouchApp()
{
	var _this = this;
	this.mTickCount = 0;
	this.mLoadCount = 2;

	this.canvas = document.getElementById("cv");
	P3D.g = this.canvas.getContext("2d");

	this.vUP   = new Vec3(0, 1, 0);
	this.vAT   = new Vec3(0, 0, 0);
	this.mEye  = new Vec3(1, 0, 15);
	this.mEyeA = new Vec3(0, 0, 20);

	this.mViewport = {};
	this.mViewport.w = 480;
	this.mViewport.h = 300;
	this.mViewport.ow = 240;
	this.mViewport.oh = 150;

	this.mProjMat  = new M44();
	this.mWorldMat = new M44();
	this.mViewMat  = new M44();
	this.mAllMat   = new M44();

	this.ipod = new iPodModel(MESH_IPOD);
	this.canvas.addEventListener("mousemove", function(e){_this.onMouseMove(e);}, false);

	var tex = new Image();
	this.ipod.texture = tex;
	tex.onload = function(){ _this.start(); };
			var rand_no = Math.floor(2*Math.random())
		if(rand_no < 1){

	tex.src = "20090226031414.png";
		}else{
	tex.src = "stephs-touch-art.png";
		}
		
// tex.src = "stephs-touch-art.png";
//	tex.src = "20090226031414.png";

	tex = new Image();
	this.texture2 = tex;
	tex.onload = function(){ _this.start(); };
	tex.src = "20090226031413.png";
}

TouchApp.prototype = {
	start: function() {
		if (--this.mLoadCount > 0) return;

		this.setupTransform();
		this.onInterval();
	},

	onInterval: function() {
		this.mEye.smul(0.7);
		this.mEye.x += this.mEyeA.x * 0.3;
		this.mEye.y += this.mEyeA.y * 0.3;
		this.mEye.z += this.mEyeA.z * 0.3;
		this.updateViewTrans();

		this.mWorldMat.ident();
		this.calcAllTransforms();
		this.transformVertices(MESH_floor);

		this.mWorldMat.glRotate(0.14, 1, 0, 0);
		this.calcAllTransforms();
		this.transformVertices(this.ipod.mesh);

		P3D.clear("#fff", this.mViewport.w, this.mViewport.h);
		this.drawFloor();
		this.ipod.render(this);

		this.mTickCount++;
		var _this = this;
		setTimeout(function(){_this.onInterval();}, 20);
	},

	updateViewTrans: function(ry) {
		this.mViewMat.lookAtLH(this.vUP, this.mEye, this.vAT);
	},

	onMouseMove: function(e) {
		this.setEyePosTarget(e.clientX - this.mViewport.ow, e.clientY - this.mViewport.oh);
	},

	setEyePosTarget: function(tx, ty) {
		var R = 20;
		this.mEyeA.y = 30 - ty*0.4;
		if (this.mEyeA.y < -0.7) {
			R += -0.7 - this.mEyeA.y;
			this.mEyeA.y = -0.7;
		}
		this.mEyeA.z = Math.cos(tx*0.02) * R;
		this.mEyeA.x = Math.sin(tx*0.02) * R;
	},

	setupTransform: function() {
		this.mWorldMat.ident();
		this.mProjMat.perspectiveLH(8, 5, 4, 900);
		this.mViewMat.ident();
	},

	calcAllTransforms: function() {
		if (!this._mTmpMat) this._mTmpMat = new M44();
		this._mTmpMat.mul(this.mWorldMat, this.mViewMat);
		this.mAllMat.mul(this._mTmpMat, this.mProjMat);
	},

	transformVertices: function(mesh) {
		var i;
		var m = this.mAllMat;
		var hw = this.mViewport.ow;
		var hh = this.mViewport.oh;

		if ((mesh.poss.length % 3) != 0)
			throw "invalid length";

		var poss = mesh.poss;
		var plen = poss.length/3;

		if (!mesh.tl_poss) {
			mesh.tl_poss = new Array(plen * 4);
		}
		var tl_poss = mesh.tl_poss;

		var pi = 0, ti = 0, spos = new Array(4), p = new Vec3();
		for (i = 0;i < plen;i++) {
			p.x = poss[pi  ];
			p.y = poss[pi+1];
			p.z = poss[pi+2];
			pi += 3;

			m.transVec3(spos, p.x, p.y, p.z);

			var W = spos[3];
			spos[0] /= W;
			spos[1] /= W;
			spos[2] /= W;

			spos[0] *= this.mViewport.w;
			spos[1] *= -this.mViewport.h;
			spos[0] += hw;
			spos[1] += hh;

			tl_poss[ti++] = spos[0];
			tl_poss[ti++] = spos[1];
			tl_poss[ti++] = spos[2];
			ti++;
		}
	},

	drawFloor: function() {
		P3D.texture = this.texture2;
// console.log(MESH_floor.tl_poss[2] + "  " + MESH_floor.tl_poss[6])
		P3D.drawByIndexBuffer(MESH_floor.tl_poss, MESH_floor.face_groups[0], MESH_floor.texcoords, -1, true);
	}
}

function iPodModel(mesh) {
	this.texture = null;
	this.mesh = mesh;
	this.uvbuf = new Array(124);
	this.sphmap_uvbuf = new Array(124);

	this.uvbuf[112] = 0;
	this.uvbuf[113] = 0;

	this.uvbuf[114] = 0;
	this.uvbuf[115] = 0.875;

	this.uvbuf[116] = 0.3333;
	this.uvbuf[117] = 0;

	this.uvbuf[118] = 0.3333;
	this.uvbuf[119] = 0.875;

	this.uvbuf[120] = 0;
	this.uvbuf[121] = 0.4375;

	this.uvbuf[122] = 0.3333;
	this.uvbuf[123] = 0.4375;
}

iPodModel.prototype = {
	render: function(renderContext) {
		P3D.texture = this.texture;
		P3D.drawByIndexBuffer(this.mesh.tl_poss, this.mesh.face_groups[0], this.uvbuf, -1);

		this.setSphereMapUVs(2, renderContext);
		P3D.drawByIndexBuffer(this.mesh.tl_poss, this.mesh.face_groups[2], this.sphmap_uvbuf, -1);
		P3D.texture = null;

		var face_plate = this.mesh.face_groups[1];
		var vi, i, len = face_plate.length;
		for (i = 0;i < len;i += 3) {
			vi = face_plate[i];
			this.uvbuf[vi << 1] = "#000";
		}

		P3D.drawByIndexBuffer(this.mesh.tl_poss, this.mesh.face_groups[1], this.uvbuf, -1);
	},

	setSphereMapUVs: function(gi, renderContext) {
		var faces = this.mesh.face_groups[gi];
		var poss  = this.mesh.poss;
		var i, len = faces.length/3;
		var v0, v1, v2, vN, vNt;
		var vlen = poss.length;

		if (!this.mesh.N_list) {
// generate N vector
			vN = new Vec3();
			var vA = new Vec3(), vB = new Vec3();
			this.mesh.N_list = new Array( poss.length );

			for (i = 0;i < vlen;i++) {
				this.mesh.N_list[i] = new Vec3(0, 0, 0);
			}

			for (i = 0;i < len;i++) {
				v0 = faces[i*3  ];
				v1 = faces[i*3+1];
				v2 = faces[i*3+2];

				vA.x = poss[v1*3  ] - poss[v0*3  ];
				vA.y = poss[v1*3+1] - poss[v0*3+1];
				vA.z = poss[v1*3+2] - poss[v0*3+2];

				vB.x = poss[v2*3  ] - poss[v1*3  ];
				vB.y = poss[v2*3+1] - poss[v1*3+1];
				vB.z = poss[v2*3+2] - poss[v1*3+2];

				vN.cp(vB, vA).normalize();

				this.mesh.N_list[v0].add(vN);
				this.mesh.N_list[v1].add(vN);
				this.mesh.N_list[v2].add(vN);
			}

			for (i = 0;i < vlen;i++) {
				this.mesh.N_list[i].normalize();
			}

// generate N vector
		}
/*
for (i = 0;i < vlen;i++) {
	vN = this.mesh.N_list[i];
	this.sphmap_uvbuf[i<<1     ] = (vN.y < -0.3) ? "#700" : (vN.y < 0.2) ? "#a00" : (vN.y < 0.4) ? "#c00" : "#f00"
}
return;
*/
		var spos = [0,0,0,0];
		var viewRot = (new M44()).copyFrom(renderContext.mViewMat).transpose33();
		var worldRot = (new M44()).copyFrom(renderContext.mWorldMat);
		viewRot._41 = viewRot._42 = viewRot._43 = 0;
		viewRot.transVec3(spos, 0, 0, 1);

		var in_vector = (new Vec3(spos[0], spos[1], spos[2])).normalize();
		var rN = new Vec3(), rlen;

		vN = new Vec3();
		for (i = 0;i < vlen;i++) {
			vNt = this.mesh.N_list[i];
			worldRot.transVec3(spos, vNt.x, vNt.y, vNt.z);
			vN.x = spos[0];
			vN.y = spos[1];
			vN.z = spos[2];

			rlen = vN.dpWith(in_vector) * -2;

			rN.copyFrom(in_vector);

			rN.x += vN.x * rlen;
			rN.y += vN.y * rlen;
			rN.z += vN.z * rlen;

			this.sphmap_uvbuf[i<<1     ] = rN.x/2*0.666 + 0.666666;
			this.sphmap_uvbuf[(i<<1)+ 1] = -rN.y/2.01 + 0.5;
		}
	}
}
