3D Mathematics

After I posted a vector object for calculating stuff in 2D space (javascript game development 1: vector), I think it would be cool to do this in 3D, too. The vector object is placed in the index.html file directly. So it’s not structured very well, but these are the things I’d ever searched for when I was trying to code an software 3D engine.

May you find it useful. As a test I created a wireframe cube. To draw it with solid surfaces, you have to calculate what surfaces hiding each other. Best practice would be, to rasterize every pixel and store it’s dept in an extra buffer (z-buffer), to terminate if new pixels in front of the old. Then add texturing, lighting, mesh loader, …

But for now, here it is:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body style="background: #333">
 
	<canvas id="screen" width="320" height="240"></canvas>
 
 
	<script>
 
	(function() {
 
 
		//3D vector object 
		var vector = (function vector(x = 0.0, y = 0.0, z = 0.0) {
 
			this.x = x;
			this.y = y;
			this.z = z;
 
			this.checkIsVector = (function(object) {
				return (object.constructor.name === "vector");
			});
 
			//+
			this.add = (function(vec) {
 
				var res = new vector(this.x, this.y, this.z);
 
				if(this.checkIsVector(vec))
				{
					res.x += vec.x;
					res.y += vec.y;
					res.z += vec.z;
					return res;
				}
 
				res.x += vec;
				res.y += vec;
				res.z += vec;
				return res;
 
			});
 
                        //-
			this.sub = (function(vec) {
 
				var res = new vector(this.x, this.y, this.z);
 
				if(this.checkIsVector(vec))
				{
					res.x -= vec.x;
					res.y -= vec.y;
					res.z -= vec.z;
					return res;
				}
 
				res.x -= vec;
				res.y -= vec;
				res.z -= vec;
				return res;
 
			});
 
                        //*
			this.mul = (function(vec) {
 
				var res = new vector(this.x, this.y, this.z);
 
				if(this.checkIsVector(vec))
				{
					res.x *= vec.x;
					res.y *= vec.y;
					res.z *= vec.z;
					return res;
				}
 
				res.x *= vec;
				res.y *= vec;
				res.z *= vec;
				return res;
 
			});
 
                        //:
			this.div = (function(vec) {
 
				var res = new vector(this.x, this.y, this.z);
 
				if(this.checkIsVector(vec))
				{
					res.x /= vec.x;
					res.y /= vec.y;
					res.z /= vec.z;
					return res;
				}
 
				res.x /= vec;
				res.y /= vec;
				res.z /= vec;
				return res;
 
			});
 
                        //magnitude
			this.length = (function(vec) {
				return Math.sqrt(Math.pow(this.x - vec.x, 2) + Math.pow(this.y - vec.y, 2) + Math.pow(this.z - vec.z, 2));
			});
 
                        //normal vector
			this.normal = (function(vec) {
 
				var l = this.length(vec);
				var v = new vector(this.x - vec.x, this.y - vec.y, this.z -vec.z);
 
				return v.div(l);
 
			});
 
		});
 
 
 
 
		//for flying around
		var pX = 0, pY = 0, pZ = 0;
 
		var pos = new vector(0, 0, 40);
		var scale = new vector(5, 5, 5);
 
                /*
                 * there are only points, no vertices
                 * that means, there are no color, texture coords, normals or other fancy things.
                 * 8 points would be enough to describe a cube
                 * but we had no face list or order to connect the points
                 * thats why we paint some lines more times (it would be an nice update)
                 */
 
		var points = [];
		points.push(new vector(-1, +1, +1));
		points.push(new vector(+1, +1, +1));
		points.push(new vector(+1, -1, +1));
		points.push(new vector(-1, -1, +1));
		points.push(new vector(-1, +1, +1));
		points.push(new vector(-1, +1, -1));
		points.push(new vector(+1, +1, -1));
		points.push(new vector(+1, +1, +1));
		points.push(new vector(+1, -1, +1));
		points.push(new vector(+1, -1, -1));
		points.push(new vector(+1, +1, -1));
		points.push(new vector(-1, +1, -1));
		points.push(new vector(-1, -1, -1));
		points.push(new vector(+1, -1, -1));
		points.push(new vector(-1, -1, -1));
		points.push(new vector(-1, -1, +1));
 
		var canvas = document.getElementById("screen");
		var ctx = canvas.getContext("2d");
		ctx.strokeStyle="#c80";
		ctx.lineWidth = 3;
		ctx.fillStyle="#f4f4f4";
 
		var angle = 0;
 
		function draw()
		{
			//clear screen
			ctx.fillRect(0, 0, canvas.width, canvas.height);
 
                        //move something
			angle += 0.5;
			pX += 0.01;
			pY += 0.02;
			pZ += 0.05;
 
			pos.x = Math.sin(pX) * 10;
			pos.y = Math.cos(pY) * 10;
			pos.z = Math.sin(pX) * 10 + 40;
 
			var radian = angle * 0.017453293;
			var drawingList = [];
 
			//transform points
			for(var i = 0; i < points.length; i++)
			{
 
				//scaling
				var v = points[i].mul(scale)
 
				//rotating
				var sinO = Math.sin(radian);
				var cosO = Math.cos(radian);
				var x,y, z;
 
				//x - axis
				y = cosO * v.z - sinO * v.y;
				z = sinO * v.z + cosO * v.y;
				v.y = y;
				v.z = z;
 
				//y - axis
				x = cosO * v.x - sinO * v.z;
				z = sinO * v.x + cosO * v.z;
				v.x = x;
				v.z = z;
 
				//z - axis
				x = cosO * v.x - sinO * v.y; 
				y = sinO * v.x + cosO * v.y; 
				v.x = x;
				v.y = y;
 
				//position transition
				v = v.add(pos);
				drawingList.push(v);
 
			}
 
 
			//draw
			for(var i = 0; i < drawingList.length; i++)
			{
 
				var v1 = drawingList[i];
				var v2 = drawingList[(i + 1) % drawingList.length];
 
				//perspective projection
				var x1 = v1.x / v1.z;
				var y1 = v1.y / v1.z;
				x1 = (x1 + 1) * (320 / 2);
				y1 = (y1 + 1) * (240 / 2);
 
				var x2 = v2.x / v2.z;
				var y2 = v2.y / v2.z;
				x2 = (x2 + 1) * (320 / 2);
				y2 = (y2 + 1) * (240 / 2);
 
 
				ctx.beginPath();
				ctx.moveTo(x1, y1);
				ctx.lineTo(x2, y2);
				ctx.stroke();
 
			}
 
 
		}
 
 
		window.setInterval(draw, 16);
 
	})();
 
	</script>
 
 
 
</body>
</html>

Have a nice day.