Browse Source

First public version. Still a lot to do

Mr.doob 15 years ago
parent
commit
a90c4e107f

+ 61 - 0
src/Class.js

@@ -0,0 +1,61 @@
+// http://ejohn.org/blog/simple-javascript-inheritance/
+
+// Inspired by base2 and Prototype
+(function(){
+  var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
+  // The base Class implementation (does nothing)
+  this.Class = function(){};
+  
+  // Create a new Class that inherits from this class
+  Class.extend = function(prop) {
+    var _super = this.prototype;
+    
+    // Instantiate a base class (but only create the instance,
+    // don't run the init constructor)
+    initializing = true;
+    var prototype = new this();
+    initializing = false;
+    
+    // Copy the properties over onto the new prototype
+    for (var name in prop) {
+      // Check if we're overwriting an existing function
+      prototype[name] = typeof prop[name] == "function" && 
+        typeof _super[name] == "function" && fnTest.test(prop[name]) ?
+        (function(name, fn){
+          return function() {
+            var tmp = this._super;
+            
+            // Add a new ._super() method that is the same method
+            // but on the super-class
+            this._super = _super[name];
+            
+            // The method only need to be bound temporarily, so we
+            // remove it when we're done executing
+            var ret = fn.apply(this, arguments);        
+            this._super = tmp;
+            
+            return ret;
+          };
+        })(name, prop[name]) :
+        prop[name];
+    }
+    
+    // The dummy class constructor
+    function Class() {
+      // All construction is actually done in the init method
+      if ( !initializing && this.init )
+        this.init.apply(this, arguments);
+    }
+    
+    // Populate our constructed prototype object
+    Class.prototype = prototype;
+    
+    // Enforce the constructor to be what we expect
+    Class.constructor = Class;
+
+    // And make this class extendable
+    Class.extend = arguments.callee;
+    
+    return Class;
+  };
+})();

+ 32 - 0
src/cameras/Camera.js

@@ -0,0 +1,32 @@
+var Camera = Vector3.extend
+({
+	up: null,
+	target: null,
+	zoom: null,
+	focus: null,
+	roll: null,
+	
+	matrix: null,
+	
+	init: function(x, y, z)
+	{
+		this._super(x, y, z);
+		this.up = new Vector3( 0, 1, 0 );
+		this.target = new Vector3( 0, 0, 0 );
+		this.zoom = 3;
+		this.focus = 500;
+		this.roll = 0;
+		
+		this.matrix = new Matrix4();
+	},
+	
+	updateMatrix: function()
+	{
+		this.matrix.lookAt( this, this.target, this.up );
+	},
+
+	toString: function()
+	{
+		return 'Camera ( ' + this.x + ', ' + this.y + ', ' + this.z + ' )';
+	}
+});

+ 31 - 0
src/core/Face3.js

@@ -0,0 +1,31 @@
+var Face3 = Vector3.extend
+({
+	a: null, b: null, c: null,
+	screen: null,
+	uv: null,
+	normal: null,
+	color: null,
+	colorString: null,
+
+	init: function(a, b, c, uv, normal, color)
+	{
+		this._super((a.x + b.x + c.x) / 3, (a.y + b.y + c.y) / 3, (a.z + b.z + c.z) / 3);	
+	
+		this.a = a;
+		this.b = b;
+		this.c = c;
+
+		this.screen = new Vector3();
+		
+		this.uv = uv ? uv : [ [0, 0], [0, 0], [0, 0] ];
+		this.normal = normal ? normal : new Vector3();
+
+		this.color = color ? color : [0, 0, 0];
+		this.colorString = 'rgb(' + this.color[0] + ', ' + this.color[1] + ', ' + this.color[2] + ')';
+	},
+
+	toString: function()
+	{
+		return 'Face ( ' + this.a + ', ' + this.b + ', ' + this.c + ' )';
+	}
+});

+ 28 - 0
src/core/Face4.js

@@ -0,0 +1,28 @@
+var Face4 = Vector3.extend
+({
+	a: null, b: null, c: null, d: null,
+	normal: null,
+	screen: null,
+	color: null,
+	colorString: null,
+
+	init: function(a, b, c, d, uv, normal, color)
+	{
+		this._super((a.x + b.x + c.x + d.x) / 4, (a.y + b.y + c.y + d.y) / 4, (a.z + b.z + c.z + d.z) / 4);
+	
+		this.a = a;
+		this.b = b;
+		this.c = c;
+		this.d = d;
+		
+		this.screen = new Vector3();
+
+		this.color = color ? color : [0, 0, 0];
+		this.colorString = 'rgb(' + this.color[0] + ', ' + this.color[1] + ', ' + this.color[2] + ')';		
+	},
+
+	toString: function()
+	{
+		return 'Face ( ' + this.a + ', ' + this.b + ', ' + this.c + ' )';
+	}
+});

+ 11 - 0
src/core/Geometry.js

@@ -0,0 +1,11 @@
+var Geometry = Class.extend
+({
+	vertices: null,
+	faces: null,
+
+	init: function()
+	{
+		this.vertices = new Array();
+		this.faces = new Array();
+	}
+});

+ 71 - 0
src/core/Matrix3.js

@@ -0,0 +1,71 @@
+var Matrix3 = Class.extend
+({
+	n11: null, n12: null, n13: null,
+	n21: null, n22: null, n23: null,
+	n31: null, n32: null, n33: null,
+
+	init: function()
+	{
+		this.identity();
+	},
+
+	identity: function()
+	{
+		this.n11 = 1; this.n12 = 0; this.n13 = 0;
+		this.n21 = 0; this.n22 = 1; this.n23 = 0;
+		this.n31 = 0; this.n32 = 0; this.n33 = 1;
+	},
+
+	assign: function(m)
+	{
+		this.n11 = m.n11; this.n12 = m.n12; this.n13 = m.n13;
+		this.n21 = m.n21; this.n22 = m.n22; this.n23 = m.n23;
+		this.n31 = m.n31; this.n32 = m.n32; this.n33 = m.n33;
+	},
+
+	multiplySelf: function(m)
+	{
+		var n11 = this.n11, n12 = this.n12, n13 = this.n13, n14 = this.n14;
+		var n21 = this.n21, n22 = this.n22, n23 = this.n23, n24 = this.n24;
+		var n31 = this.n31, n32 = this.n32, n33 = this.n33, n34 = this.n34;
+
+		this.n11 = n11 * m.n11 + n12 * m.n21 + n13 * m.n31;
+		this.n12 = n11 * m.n12 + n12 * m.n22 + n13 * m.n32;
+		this.n13 = n11 * m.n13 + n12 * m.n23 + n13 * m.n33;
+
+		this.n21 = n21 * m.n11 + n22 * m.n21 + n23 * m.n31;
+		this.n22 = n21 * m.n12 + n22 * m.n22 + n23 * m.n32;
+		this.n23 = n21 * m.n13 + n22 * m.n23 + n23 * m.n33;
+
+		this.n31 = n31 * m.n11 + n32 * m.n21 + n33 * m.n31;
+		this.n32 = n31 * m.n12 + n32 * m.n22 + n33 * m.n32;
+		this.n33 = n31 * m.n13 + n32 * m.n23 + n33 * m.n33;
+	},
+
+	inverse: function()
+	{
+		var n11 = this.n11, n12 = this.n12, n13 = this.n13, n14 = this.n14;
+		var n21 = this.n21, n22 = this.n22, n23 = this.n23, n24 = this.n24;
+		var n31 = this.n31, n32 = this.n32, n33 = this.n33, n34 = this.n34;
+
+		this.n11 = n11; this.n12 = n21; this.n13 = n31;
+		this.n21 = n12; this.n22 = n22; this.n23 = n32;
+		this.n31 = n13; this.n32 = n23; this.n33 = n33;
+	},
+
+	clone: function()
+	{
+		var m = new Matrix3();
+		m.n11 = this.n11; m.n12 = this.n12; m.n13 = this.n13;
+		m.n21 = this.n21; m.n22 = this.n22; m.n23 = this.n23;
+		m.n31 = this.n31; m.n32 = this.n32; m.n33 = this.n33;
+		return m;
+	},
+    
+	toString: function()
+	{
+        	return "| " + this.n11 + " " + this.n12 + " " + this.n13 + " |\n" +
+                        "| " + this.n21 + " " + this.n22 + " " + this.n23 + " |\n" +
+                        "| " + this.n31 + " " + this.n32 + " " + this.n33 + " |";
+	}
+});

+ 200 - 0
src/core/Matrix4.js

@@ -0,0 +1,200 @@
+var Matrix4 = Class.extend
+({
+	n11: null, n12: null, n13: null, n14: null,
+	n21: null, n22: null, n23: null, n24: null,
+	n31: null, n32: null, n33: null, n34: null,
+
+	x: null, y: null, z: null,
+
+	init: function()
+	{
+		this.identity();
+	},
+
+	identity: function()
+	{
+		this.n11 = 1; this.n12 = 0; this.n13 = 0; this.n14 = 0;
+		this.n21 = 0; this.n22 = 1; this.n23 = 0; this.n24 = 0;
+		this.n31 = 0; this.n32 = 0; this.n33 = 1; this.n34 = 0;
+		
+		this.x = new Vector3(0,0,0);
+		this.y = new Vector3(0,0,0);
+		this.z = new Vector3(0,0,0);
+	},
+    
+	lookAt: function(eye, center, up)
+	{
+		this.z.sub(center, eye);
+		this.z.normalize();
+
+		this.x.copy(this.z);
+		this.x.cross(up);
+		this.x.normalize();
+
+		this.y.copy(this.x);
+		this.y.cross(this.z);
+		this.y.normalize();
+		this.y.negate(); //
+
+		this.n11 = this.x.x;
+		this.n12 = this.x.y;
+		this.n13 = this.x.z;
+		this.n14 = this.x.dot(eye);
+		this.n21 = this.y.x;
+		this.n22 = this.y.y;
+		this.n23 = this.y.z;
+		this.n24 = this.y.dot(eye);
+		this.n31 = this.z.x;
+		this.n32 = this.z.y;
+		this.n33 = this.z.z;
+		this.n34 = -this.z.dot(eye);
+	},
+
+	transform: function(v)
+	{
+        	var vx = v.x, vy = v.y, vz = v.z;
+        
+		v.x = this.n11 * vx + this.n12 * vy + this.n13 * vz + this.n14;
+		v.y = this.n21 * vx + this.n22 * vy + this.n23 * vz + this.n24;
+		v.z = this.n31 * vx + this.n32 * vy + this.n33 * vz + this.n34;
+	},
+    
+
+	multiply: function(a, b)
+	{
+		this.n11 = a.n11 * b.n11 + a.n12 * b.n21 + a.n13 * b.n31;
+		this.n12 = a.n11 * b.n12 + a.n12 * b.n22 + a.n13 * b.n32;
+		this.n13 = a.n11 * b.n13 + a.n12 * b.n23 + a.n13 * b.n33;
+		this.n14 = a.n11 * b.n14 + a.n12 * b.n24 + a.n13 * b.n34 + a.n14;
+
+		this.n21 = a.n21 * b.n11 + a.n22 * b.n21 + a.n23 * b.n31;
+		this.n22 = a.n21 * b.n12 + a.n22 * b.n22 + a.n23 * b.n32;
+		this.n23 = a.n21 * b.n13 + a.n22 * b.n23 + a.n23 * b.n33;
+		this.n24 = a.n21 * b.n14 + a.n22 * b.n24 + a.n23 * b.n34 + a.n24;
+
+		this.n31 = a.n31 * b.n11 + a.n32 * b.n21 + a.n33 * b.n31;
+		this.n32 = a.n31 * b.n12 + a.n32 * b.n22 + a.n33 * b.n32;
+		this.n33 = a.n31 * b.n13 + a.n32 * b.n23 + a.n33 * b.n33;
+		this.n34 = a.n31 * b.n14 + a.n32 * b.n24 + a.n33 * b.n34 + a.n34;
+	},
+
+	multiplySelf: function(m)
+	{
+		var n11 = this.n11, n12 = this.n12, n13 = this.n13, n14 = this.n14;
+		var n21 = this.n21, n22 = this.n22, n23 = this.n23, n24 = this.n24;
+		var n31 = this.n31, n32 = this.n32, n33 = this.n33, n34 = this.n34;
+
+		this.n11 = n11 * m.n11 + n12 * m.n21 + n13 * m.n31;
+		this.n12 = n11 * m.n12 + n12 * m.n22 + n13 * m.n32;
+		this.n13 = n11 * m.n13 + n12 * m.n23 + n13 * m.n33;
+		this.n14 = n11 * m.n14 + n12 * m.n24 + n13 * m.n34 + n14;
+
+		this.n21 = n21 * m.n11 + n22 * m.n21 + n23 * m.n31;
+		this.n22 = n21 * m.n12 + n22 * m.n22 + n23 * m.n32;
+		this.n23 = n21 * m.n13 + n22 * m.n23 + n23 * m.n33;
+		this.n24 = n21 * m.n14 + n22 * m.n24 + n23 * m.n34 + n24;
+
+		this.n31 = n31 * m.n11 + n32 * m.n21 + n33 * m.n31;
+		this.n32 = n31 * m.n12 + n32 * m.n22 + n33 * m.n32;
+		this.n33 = n31 * m.n13 + n32 * m.n23 + n33 * m.n33;
+		this.n34 = n31 * m.n14 + n32 * m.n24 + n33 * m.n34 + n34;
+	},
+
+	clone: function()
+	{
+		var m = new Matrix4();
+		m.n11 = this.n11; m.n12 = this.n12; m.n13 = this.n13; m.n14 = this.n14;
+		m.n21 = this.n21; m.n22 = this.n22; m.n23 = this.n23; m.n24 = this.n24;
+		m.n31 = this.n31; m.n32 = this.n32; m.n33 = this.n33; m.n34 = this.n34;
+		return m;
+	},
+    
+	toString: function()
+	{
+        	return "| " + this.n11 + " " + this.n12 + " " + this.n13 + " " + this.n14 + " |\n" +
+                        "| " + this.n21 + " " + this.n22 + " " + this.n23 + " " + this.n24 + " |\n" +
+                        "| " + this.n31 + " " + this.n32 + " " + this.n33 + " " + this.n34 + " |";
+	}
+});
+
+Matrix4.translationMatrix = function(x, y, z)
+{
+	var m = new Matrix4();
+
+	m.n14 = x;
+	m.n24 = y;
+	m.n34 = z;
+
+	return m;
+}
+
+Matrix4.scaleMatrix = function(x, y, z)
+{
+	var m = new Matrix4();
+
+	m.n11 = x;
+	m.n22 = y;
+	m.n33 = z;
+
+	return m;
+}
+
+Matrix4.rotationXMatrix = function(theta)
+{
+	var rot = new Matrix4();
+
+	rot.n22 = rot.n33 = Math.cos(theta);
+	rot.n32 = Math.sin(theta);
+	rot.n23 = -rot.n32;
+
+	return rot;
+}
+
+Matrix4.rotationYMatrix = function(theta)
+{
+	var rot = new Matrix4();
+
+	rot.n11 = rot.n33 = Math.cos(theta);
+	rot.n13 = Math.sin(theta);
+	rot.n31 = -rot.n13;
+
+	return rot;
+}
+
+Matrix4.rotationZMatrix = function(theta)
+{
+	var rot = new Matrix4();
+
+	rot.n11 = rot.n22 = Math.cos(theta);
+	rot.n21 = Math.sin(theta);
+	rot.n12 = -rot.n21;
+
+	return rot;
+}
+
+Matrix4.rotationMatrix = function(ry, rx, rz)
+{
+	var sx, sy, sz;
+	var cx, cy, cz;
+
+	sx = Math.sin(rx);
+	sy = Math.sin(ry);
+	sz = Math.sin(rz);
+	cx = Math.cos(rx);
+	cy = Math.cos(ry);
+	cz = Math.cos(rz);
+
+	var rot = new Matrix4();
+
+	rot.n11 = cx * cz - sx * sy * sz;
+	rot.n12 = -cy * sz;
+	rot.n13 = sx * cz + cx * sy * sz;
+	rot.n21 = cx * sz + sx * sy * cz;
+	rot.n22 = cy * cz;
+	rot.n23 = sx * sz - cx * sy * cz;
+	rot.n31 = -sx * cy;
+	rot.n32 = sy;
+	rot.n33 = cx * cy;
+
+	return rot;
+}

+ 100 - 0
src/core/Vector2.js

@@ -0,0 +1,100 @@
+var Vector2 = Class.extend
+({
+	x: null, y: null,
+	
+	init: function(x, y)
+	{
+		this.x = x ? x : 0;
+		this.y = y ? y : 0;
+	},
+
+	copy: function(v)
+	{
+		this.x = v.x;
+		this.y = v.y;
+	},
+	
+	addSelf: function(v)
+	{
+		this.x += v.x;
+		this.y += v.y;
+	},
+
+	add: function(v1, v2)
+	{
+		this.x = v1.x + v2.x;
+		this.y = v1.y + v2.y;
+	},
+	
+	subSelf: function(v)
+	{
+		this.x -= v.x;
+		this.y -= v.y;
+	},
+
+	sub: function(v1, v2)
+	{
+		this.x = v1.x - v2.x;
+		this.y = v1.y - v2.y;
+	},
+	
+	multiply: function(s)
+	{
+		this.x *= s;
+		this.y *= s;
+	},
+	
+	unit: function()
+	{
+		this.multiply(1 / this.length());
+	},
+	
+	expand: function(v1, v2)
+	{
+		this.unit( this.sub(v2, v1) );
+		v2.addSelf(this);
+		// v1.subSelf(this);
+	},
+
+	length: function()
+	{
+		return Math.sqrt(this.x * this.x + this.y * this.y);
+	},
+	
+	lengthSq: function()
+	{
+		return this.x * this.x + this.y * this.y;
+	},
+	
+	negate: function()
+	{
+		this.x = -this.x;
+		this.y = -this.y;
+	},
+	
+	clone: function()
+	{
+		return new Vector2(this.x, this.y);
+	},	
+	
+	toString: function()
+	{
+		return 'Vector2 (' + this.x + ', ' + this.y + ')';
+	}
+	
+});
+
+Vector2.add = function(a, b)
+{
+	return new Vector2( a.x + b.x, a.y + b.y );
+}
+
+Vector2.sub = function(a, b)
+{
+	return new Vector2( a.x - b.x, a.y - b.y );
+}		
+
+Vector2.multiply = function(a, s)
+{
+	return new Vector2( a.x * s, a.y * s );
+}

+ 159 - 0
src/core/Vector3.js

@@ -0,0 +1,159 @@
+var Vector3 = Class.extend
+({
+	x: null, y: null, z: null,
+	// sx: null, sy: null, sz: null,
+	// userData: null,
+	
+	dx: null, dy: null, dz: null,
+	tx: null, ty: null, tz: null,
+	// oll: null,
+	
+	init: function(x, y, z)
+	{
+		this.x = x ? x : 0;
+		this.y = y ? y : 0;
+		this.z = z ? z : 0;
+	},
+
+	copy: function(v)
+	{
+		this.x = v.x;
+		this.y = v.y;
+		this.z = v.z;
+	},
+	
+	addSelf: function(v)
+	{
+		this.x += v.x;
+		this.y += v.y;
+		this.z += v.z;
+	},
+
+	add: function(v1, v2)
+	{
+		this.x = v1.x + v2.x;
+		this.y = v1.y + v2.y;
+		this.z = v1.z + v2.z;
+	},
+	
+	subSelf: function(v)
+	{
+		this.x -= v.x;
+		this.y -= v.y;
+		this.z -= v.z;
+	},
+
+	sub: function(v1, v2)
+	{
+		this.x = v1.x - v2.x;
+		this.y = v1.y - v2.y;
+		this.z = v1.z - v2.z;
+	},
+	
+	cross: function(v)
+	{
+		this.tx = this.x;
+		this.ty = this.y;
+		this.tz = this.z;
+		
+		this.x = this.ty * v.z - this.tz * v.y;
+		this.y = this.tz * v.x - this.tx * v.z;
+		this.z = this.tx * v.y - this.ty * v.x;
+	},
+	
+	multiply: function(s)
+	{
+		this.x *= s;
+		this.y *= s;
+		this.z *= s;
+	},
+	
+	distanceTo: function(v)
+	{
+		this.dx = this.x - v.x;
+		this.dy = this.y - v.y;
+		this.dz = this.z - v.z;
+		
+		return Math.sqrt(this.dx * this.dx + this.dy * this.dy + this.dz * this.dz);
+	},
+	
+	distanceToSquared: function(v)
+	{
+		this.dx = this.x - v.x;
+		this.dy = this.y - v.y;
+		this.dz = this.z - v.z;
+		
+		return this.dx * this.dx + this.dy * this.dy + this.dz * this.dz;
+	},
+	
+	length: function()
+	{
+		return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
+	},
+	
+	lengthSq: function()
+	{
+		return this.x * this.x + this.y * this.y + this.z * this.z;
+	},
+	
+	negate: function()
+	{
+		this.x = -this.x;
+		this.y = -this.y;
+		this.z = -this.z;
+	},
+	
+	normalize: function()
+	{
+		if (this.length() > 0)
+			this.ool = 1.0 / this.length();
+		else
+			this.ool = 0;
+			
+		this.x *= this.ool;
+		this.y *= this.ool;
+		this.z *= this.ool;
+		return this;
+	},
+	
+	dot: function(v)
+	{
+		return this.x * v.x + this.y * v.y + this.z * v.z;
+	},
+	
+	clone: function()
+	{
+		return new Vector3(this.x, this.y, this.z);
+	},	
+	
+	toString: function()
+	{
+		return 'Vector3 (' + this.x + ', ' + this.y + ', ' + this.z + ')';
+	}
+	
+});
+
+Vector3.add = function(a, b)
+{
+	return new Vector3( a.x + b.x, a.y + b.y, a.z + b.z );
+}
+
+Vector3.sub = function(a, b)
+{
+	return new Vector3( a.x - b.x, a.y - b.y, a.z - b.z );
+}		
+
+Vector3.multiply = function(a, s)
+{
+	return new Vector3( a.x * s, a.y * s, a.z * s );
+}
+
+Vector3.cross = function(a, b)
+{
+	return new Vector3( a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x );
+}
+
+Vector3.dot = function(a, b)
+{
+	return a.x * b.x + a.y * b.y + a.z * b.z;
+}

+ 18 - 0
src/core/Vertex.js

@@ -0,0 +1,18 @@
+var Vertex = Vector3.extend
+({
+	u: null, v: null,
+	screen: null,
+	normal : null,
+	visible: null,
+
+	init: function(x, y, z)
+	{
+		this._super(x, y, z);
+		this.screen = new Vector3();
+	},
+
+	toString: function()
+	{
+		return 'Vertex ( ' + this.x + ', ' + this.y + ', ' + this.z + ' )';
+	}
+});

+ 32 - 0
src/materials/ColorMaterial.js

@@ -0,0 +1,32 @@
+var ColorMaterial = Class.extend
+({
+	color: null,
+	opacity: null,
+	
+	colorString: null,	
+
+	// Uses hex instead of rgb is for keeping the syntax similar to .as version
+	
+	init: function( color, opacity )
+	{
+		this.setColor( color ? color : 0xff0000 );
+		this.setOpacity( opacity ? opacity : 1 );
+	},
+	
+	setColor: function( color )
+	{
+		this.color = color;
+		this.updateColorString();
+	},
+	
+	setOpacity: function( opacity )
+	{
+		this.opacity = opacity;
+		this.updateColorString();		
+	},
+	
+	updateColorString: function()
+	{
+		this.colorString = 'rgba(' + (this.color >> 16 & 0xff) + ',' + (this.color >> 8 & 0xff) + ',' + (this.color & 0xff) + ',' + this.opacity + ')';
+	}
+});

+ 14 - 0
src/objects/Mesh.js

@@ -0,0 +1,14 @@
+var Mesh = Object3D.extend
+({
+	geometry: null,
+	material: null,
+	
+	doubleSide: null,
+
+	init: function( geometry, material )
+	{
+		this._super();
+		this.geometry = geometry;
+		this.material = material;
+	}
+});

+ 26 - 0
src/objects/Object3D.js

@@ -0,0 +1,26 @@
+var Object3D = Class.extend
+({
+	position: null,
+	rotation: null,
+	scale: null,
+
+	matrix: null,
+	screen: null,
+
+	init: function()
+	{
+		this.position = new Vector3(0, 0, 0);
+		this.rotation = new Vector3(0, 0, 0);
+		this.scale = new Vector3(1, 1, 1);
+
+		this.matrix = new Matrix4();
+		this.screen = new Vector3(0, 0, 0);		
+	},
+
+	updateMatrix: function()
+	{
+		this.matrix.identity();
+
		this.matrix.multiplySelf( Matrix4.translationMatrix( this.position.x, this.position.y, this.position.z) );
+		this.matrix.multiplySelf( Matrix4.rotationMatrix( this.rotation.x, this.rotation.y, this.rotation.z ) );		
+		this.matrix.multiplySelf( Matrix4.scaleMatrix( this.scale.x, this.scale.y, this.scale.z ) );
	}
+});

+ 11 - 0
src/objects/Particle.js

@@ -0,0 +1,11 @@
+var Particle = Object3D.extend
+({
+	size: 1,
+	material: null,
+
+	init: function( material )
+	{
+		this._super();
+		this.material = material;
+	}
+});

+ 17 - 0
src/objects/primitives/Plane.js

@@ -0,0 +1,17 @@
+var Plane = Geometry.extend
+({
+	init: function( width, height )
+	{
+		this._super();
+		
+		var width_half = width / 2;
+		var height_half = height / 2;
+		
+		this.vertices.push( new Vertex(-width_half,  height_half, 0) );
+		this.vertices.push( new Vertex( width_half,  height_half, 0) );
+		this.vertices.push( new Vertex( width_half, -height_half, 0) );
+		this.vertices.push( new Vertex(-width_half, -height_half, 0) );
+		
+		this.faces.push( new Face4( this.vertices[0], this.vertices[1], this.vertices[2], this.vertices[3] ) );
+	}
+});

+ 69 - 0
src/renderers/CanvasRenderer.js

@@ -0,0 +1,69 @@
+var CanvasRenderer = Renderer.extend
+({
+	context: null,
+
+	init: function()
+	{
+		this._super();
+
+		this.viewport = document.createElement("canvas");
+		this.viewport.style.position = "absolute";
+
+		this.context = this.viewport.getContext("2d");
+	},
+
+	setSize: function( width, height )
+	{
+		this._super( width, height );
+
+		this.viewport.width = this.width;
+		this.viewport.height = this.height;
+	},
+
+	render: function( scene, camera )
+	{
+		this._super( scene, camera );
+
+		var element , pi2 = Math.PI * 2;
+
+		this.context.clearRect (0, 0, this.width, this.height);
+		
+		for (j = 0; j < this.renderList.length; j++)
+		{
+			element = this.renderList[j];
+			
+			if (element.material instanceof ColorMaterial)
+			{
+				this.context.fillStyle = element.material.colorString;
+			}
+			
+			if (element instanceof Face3)
+			{
+				this.context.beginPath();
+				this.context.moveTo(element.a.screen.x + this.widthHalf, element.a.screen.y + this.heightHalf);
+				this.context.lineTo(element.b.screen.x + this.widthHalf, element.b.screen.y + this.heightHalf);
+				this.context.lineTo(element.c.screen.x + this.widthHalf, element.c.screen.y + this.heightHalf);
+				this.context.fill();
+				this.context.closePath();
+			}
+			else if (element instanceof Face4)
+			{
+				this.context.beginPath();
+				this.context.moveTo(element.a.screen.x + this.widthHalf, element.a.screen.y + this.heightHalf);
+				this.context.lineTo(element.b.screen.x + this.widthHalf, element.b.screen.y + this.heightHalf);
+				this.context.lineTo(element.c.screen.x + this.widthHalf, element.c.screen.y + this.heightHalf);
+				this.context.lineTo(element.d.screen.x + this.widthHalf, element.d.screen.y + this.heightHalf);
+				this.context.fill();
+				this.context.closePath();
+			}
+			else if (element instanceof Particle)
+			{
+				this.context.beginPath();
+				this.context.arc(element.screen.x + this.widthHalf, element.screen.y + this.heightHalf, element.size * element.screen.z, 0, pi2, true);
+				this.context.fill();
+				this.context.closePath();				
+			}
+			
+		}
+	}
+});

+ 150 - 0
src/renderers/Renderer.js

@@ -0,0 +1,150 @@
+var Renderer = Class.extend
+({
+	matrix: null,
+
+	viewport: null,
+	renderList: null,
+
+	face3Pool: null,
+	face4Pool: null,
+
+	width: null,
+	height: null,
+	widthHalf: null,
+	heightHalf: null,
+
+	init: function()
+	{
+		this.matrix = new Matrix4();
+
+		this.face3Pool = new Array();
+		this.face4Pool = new Array();
+	},
+
+	setSize: function( width, height )
+	{
+		this.width = width;
+		this.height = height;
+
+		this.widthHalf = this.width / 2;
+		this.heightHalf = this.height / 2;
+	},
+
+	sort: function(a, b)
+	{
+		return a.screen.z - b.screen.z;
+	},
+
+	render: function( scene, camera )
+	{
+		var vertex, face, object;
+		var face3count = 0, face4count = 0;
+
+		var focuszoom = camera.focus * camera.zoom;
+
+		this.renderList = new Array();
+
+		for (var i = 0; i < scene.objects.length; i++)
+		{
+			object = scene.objects[i];
+
+			if (object instanceof Mesh)
+			{
+				this.matrix.multiply( camera.matrix, object.matrix );
+
+				// vertices
+
+				for (var j = 0; j < object.geometry.vertices.length; j++)
+				{
+					vertex = object.geometry.vertices[j];
+
+					vertex.screen.copy( vertex );
+
+					this.matrix.transform( vertex.screen );
+
+					vertex.screen.z = focuszoom / (camera.focus + vertex.screen.z);
+
+					vertex.visible = vertex.screen.z > 0;					
+
+					vertex.screen.x *= vertex.screen.z;
+					vertex.screen.y *= vertex.screen.z; 
+				}
+
+				// faces
+
+				for (j = 0; j < object.geometry.faces.length; j++)
+				{
+					face = object.geometry.faces[j];
+					
+					// TODO: Use normals for culling
+
+					if (face instanceof Face3)
+					{
+						if (face.a.visible && face.b.visible && face.c.visible && (object.doubleSided ||
+						   (face.c.screen.x - face.a.screen.x) * (face.b.screen.y - face.a.screen.y) -
+						   (face.c.screen.y - face.a.screen.y) * (face.b.screen.x - face.a.screen.x) > 0) )
+						{
+							face.screen.z = Math.max(face.a.screen.z, Math.max(face.b.screen.z, face.c.screen.z));
+							
+							if (this.face3Pool[face3count] == null)
+								this.face3Pool[face3count] = new Face3(new Vertex(), new Vertex(), new Vertex());
+
+							this.face3Pool[face3count].a.screen.copy(face.a.screen);
+							this.face3Pool[face3count].b.screen.copy(face.b.screen);
+							this.face3Pool[face3count].c.screen.copy(face.c.screen);
+							this.face3Pool[face3count].screen.z = face.screen.z;
+							this.face3Pool[face3count].material = face.material;
+
+							this.renderList.push( this.face3Pool[face3count] );
+
+							face3count++;
+						}
+					}
+					else if (face instanceof Face4)
+					{
+						if (face.a.visible && face.b.visible && face.c.visible && (object.doubleSided ||
+						   ((face.d.screen.x - face.a.screen.x) * (face.b.screen.y - face.a.screen.y) -
+						   (face.d.screen.y - face.a.screen.y) * (face.b.screen.x - face.a.screen.x) > 0 ||
+						   (face.b.screen.x - face.c.screen.x) * (face.d.screen.y - face.c.screen.y) -
+						   (face.b.screen.y - face.c.screen.y) * (face.d.screen.x - face.c.screen.x) > 0)) )
+						{
+							face.screen.z = Math.max(face.a.screen.z, Math.max(face.b.screen.z, face.c.screen.z));
+
+							if (this.face4Pool[face4count] == null)
+								this.face4Pool[face4count] = new Face4(new Vertex(), new Vertex(), new Vertex(), new Vertex());
+
+							this.face4Pool[face4count].a.screen.copy(face.a.screen);
+							this.face4Pool[face4count].b.screen.copy(face.b.screen);
+							this.face4Pool[face4count].c.screen.copy(face.c.screen);
+							this.face4Pool[face4count].d.screen.copy(face.d.screen);
+							this.face4Pool[face4count].screen.z = face.screen.z;
+							this.face4Pool[face4count].material = face.material;
+
+							this.renderList.push( this.face4Pool[face4count] );
+
+							face4count++;
+						}						
+					}
+				}
+			}
+			else if (object instanceof Particle)
+			{
+				object.screen.copy(object.position);
+
+				camera.matrix.transform( object.screen );
+
+				object.screen.z = focuszoom / (camera.focus + object.screen.z);
+
+				if (object.screen.z < 0)
+					continue;					
+
+				object.screen.x *= object.screen.z;
+				object.screen.y *= object.screen.z;
+
+				this.renderList.push( object );
+			}
+		}
+
+		this.renderList.sort(this.sort);
+	}
+});

+ 81 - 0
src/renderers/SVGRenderer.js

@@ -0,0 +1,81 @@
+var SVGRenderer = Renderer.extend
+({
+	defs: null,
+
+	svgImagePool: null,
+	svgPatternPool: null,
+	svgPathPool: null,
+
+	init: function()
+	{
+		this._super();
+
+		this.viewport = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
+		this.viewport.style.position = "absolute";
+
+		this.svgPathPool = new Array();
+		this.svgImagePool = new Array();
+
+		this.defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs');
+
+		var texture = document.createElementNS('http://www.w3.org/2000/svg', 'pattern');
+		texture.setAttribute('id', 'texture');
+		texture.setAttribute('patternUnits', 'userSpaceOnUse');
+		texture.setAttribute('width', 552);
+		texture.setAttribute('height', 552);
+
+		var bitmap = document.createElementNS('http://www.w3.org/2000/svg', 'image');
+		bitmap.setAttribute('width', 552);
+		bitmap.setAttribute('height', 552);
+		bitmap.setAttributeNS('http://www.w3.org/1999/xlink', 'href', 'assets/uvmap.jpg');
+		texture.appendChild(bitmap);
+
+		this.svgImagePool.push(texture);
+	},
+
+	setSize: function( width, height )
+	{
+		this.viewport.setAttribute('viewBox', (-width / 2) + ' ' + (-height / 2) + ' ' + width + ' ' + height );
+		this.viewport.setAttribute('width', width);
+		this.viewport.setAttribute('height', height);
+	},
+	
+	render: function( scene, camera )
+	{
+		this._super( scene, camera );
+
+		while (this.viewport.childNodes.length > 0)
+		{
+			this.viewport.removeChild(this.viewport.childNodes[0]);
+		}
+		
+		this.viewport.appendChild(this.defs);
+		this.defs.appendChild(this.svgImagePool[0]);
+		
+		for (j = 0; j < this.renderList.length; j++)
+		{
+			element = this.renderList[j];
+
+			if (this.svgPathPool[j] == null)
+			{
+				this.svgPathPool[j] = document.createElementNS('http://www.w3.org/2000/svg', 'path');
+				this.svgPathPool[j].setAttribute('shape-rendering', 'crispEdges'); //optimizeSpeed
+			}
+
+			// this.svgPathPool[j].setAttribute('style', 'fill:url(#texture)');
+
+			this.svgPathPool[j].setAttribute('style', 'fill: rgb(' + element.color[0] + ', ' + element.color[1] + ', ' + element.color[2] + ')'); //fill-opacity:' + 0.5); // + ';stroke:' + element.color + ';stroke-width:10;stroke-opacity:0.5;'); //stroke-miterlimit:40;stroke-dasharray:5');
+
+			if (element instanceof Face3)
+			{
+				this.svgPathPool[j].setAttribute('d', 'M ' + element.a.screen.x + ' ' + element.a.screen.y + ' L ' + element.b.screen.x + ' ' + element.b.screen.y + ' L ' + element.c.screen.x + ',' + element.c.screen.y + 'z');					
+			}
+			else if (element instanceof Face4)
+			{
+				this.svgPathPool[j].setAttribute('d', 'M ' + element.a.screen.x + ' ' + element.a.screen.y + ' L ' + element.b.screen.x + ' ' + element.b.screen.y + ' L ' + element.c.screen.x + ',' + element.c.screen.y + ' L ' + element.d.screen.x + ',' + element.d.screen.y + 'z');
+			}
+
+			this.viewport.appendChild(this.svgPathPool[j]);
+		}
+	}
+});

+ 26 - 0
src/scenes/Scene.js

@@ -0,0 +1,26 @@
+var Scene = Class.extend
+({
+	objects: null,
+
+	init: function()
+	{
+		this.objects = new Array();
+	},
+
+	add: function( object )
+	{
+		this.objects.push( object );
+	},
+
+	remove: function( object )
+	{
+		for(var i = 0; i < this.objects.length; i++)
+			if(object == this.objects[i])
+				alert("yay");
+	},
+
+	toString: function()
+	{
+		return 'Scene ( ' + this.objects + ' )';
+	}
+});