فهرست منبع

Merge remote-tracking branch 'bhouston/euler3' into dev

Mr.doob 12 سال پیش
والد
کامیت
88d4eb713d

+ 2 - 2
examples/css3d_molecules.html

@@ -433,7 +433,7 @@
 
 						var objMatrix = new THREE.Matrix4().makeRotationAxis( axis.normalize(), radians );
 						object.matrix = objMatrix;
-						object.rotation.setEulerFromRotationMatrix( object.matrix, object.eulerOrder );
+						object.rotation.setFromRotationMatrix( object.matrix, object.rotation.order );
 
 						object.matrixAutoUpdate = false;
 						object.updateMatrix();
@@ -453,7 +453,7 @@
 						joint.position.lerp( end, 0.5 );
 
 						joint.matrix.copy( objMatrix );
-						joint.rotation.setEulerFromRotationMatrix( joint.matrix, joint.eulerOrder );
+						joint.rotation.setFromRotationMatrix( joint.matrix, joint.rotation.order );
 
 						joint.matrixAutoUpdate = false;
 						joint.updateMatrix();

+ 1 - 3
examples/js/controls/FlyControls.js

@@ -19,8 +19,6 @@ THREE.FlyControls = function ( object, domElement ) {
 
 	// disable default target object behavior
 
-	this.object.useQuaternion = true;
-
 	// internals
 
 	this.tmpQuaternion = new THREE.Quaternion();
@@ -199,7 +197,7 @@ THREE.FlyControls = function ( object, domElement ) {
 		this.object.quaternion.multiply( this.tmpQuaternion );
 
 		// expose the rotation vector for convenience
-		this.object.rotation.setEulerFromQuaternion( this.object.quaternion, this.object.eulerOrder );
+		this.object.rotation.setFromQuaternion( this.object.quaternion, this.object.rotation.order );
 
 
 	};

+ 8 - 8
examples/js/controls/TransformControls.js

@@ -379,11 +379,11 @@ THREE.TransformControls = function ( camera, domElement, doc ) {
 
 		this.object.updateMatrixWorld();
 		worldPosition.getPositionFromMatrix( this.object.matrixWorld );
-		worldRotation.setEulerFromRotationMatrix( tempMatrix.extractRotation(this.object.matrixWorld ));
+		worldRotation.setFromRotationMatrix( tempMatrix.extractRotation(this.object.matrixWorld ));
 
 		this.camera.updateMatrixWorld();
 		camPosition.getPositionFromMatrix( this.camera.matrixWorld );
-		camRotation.setEulerFromRotationMatrix( tempMatrix.extractRotation( this.camera.matrixWorld ));
+		camRotation.setFromRotationMatrix( tempMatrix.extractRotation( this.camera.matrixWorld ));
 
 		scale = worldPosition.distanceTo( camPosition ) / 6 * this.scale;
 		this.gizmo.position.copy( worldPosition )
@@ -399,7 +399,7 @@ THREE.TransformControls = function ( camera, domElement, doc ) {
 				if ( name.search('E') != -1 ){
 
 					lookAtMatrix.lookAt( camPosition, worldPosition, tempVector.set( 0, 1, 0 ));
-					object.rotation.setEulerFromRotationMatrix( lookAtMatrix );
+					object.rotation.setFromRotationMatrix( lookAtMatrix );
 
 				} else {
 
@@ -431,7 +431,7 @@ THREE.TransformControls = function ( camera, domElement, doc ) {
 
 						}
 
-						object.rotation.setEulerFromQuaternion( tempQuaternion );
+						object.rotation.setFromQuaternion( tempQuaternion );
 
 					} else if ( this.space == 'world' ) {
 
@@ -789,7 +789,7 @@ THREE.TransformControls = function ( camera, domElement, doc ) {
 						tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionE );
 						tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionXYZ );
 
-						scope.object.rotation.setEulerFromQuaternion( tempQuaternion );
+						scope.object.rotation.setFromQuaternion( tempQuaternion );
 
 					} else if ( scope.active == "RXYZE" ) {
 
@@ -802,7 +802,7 @@ THREE.TransformControls = function ( camera, domElement, doc ) {
 						tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionX );
 						tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionXYZ );
 
-						scope.object.rotation.setEulerFromQuaternion( tempQuaternion );
+						scope.object.rotation.setFromQuaternion( tempQuaternion );
 
 					} else if ( scope.space == 'local' ) {
 
@@ -822,7 +822,7 @@ THREE.TransformControls = function ( camera, domElement, doc ) {
 						if ( scope.active == "RY" ) quaternionXYZ.multiplyQuaternions( quaternionXYZ, quaternionY );
 						if ( scope.active == "RZ" ) quaternionXYZ.multiplyQuaternions( quaternionXYZ, quaternionZ );
 
-						scope.object.rotation.setEulerFromQuaternion( quaternionXYZ );
+						scope.object.rotation.setFromQuaternion( quaternionXYZ );
 
 					} else if ( scope.space == 'world' ) {
 
@@ -842,7 +842,7 @@ THREE.TransformControls = function ( camera, domElement, doc ) {
 
 						tempQuaternion.multiplyQuaternions( tempQuaternion, quaternionXYZ );
 
-						scope.object.rotation.setEulerFromQuaternion( tempQuaternion );
+						scope.object.rotation.setFromQuaternion( tempQuaternion );
 
 					}
 

+ 0 - 1
examples/js/loaders/ColladaLoader.js

@@ -890,7 +890,6 @@ THREE.ColladaLoader = function () {
 		var props = node.matrix.decompose();
 		obj.position = props[ 0 ];
 		obj.quaternion = props[ 1 ];
-		obj.useQuaternion = true;
 		obj.scale = props[ 2 ];
 
 		if ( options.centerGeometry && obj.geometry ) {

+ 1 - 1
examples/webgl_geometry_extrude_splines.html

@@ -382,7 +382,7 @@
 			if (!lookAhead)
 			lookAt.copy( pos ).add( dir );
 			splineCamera.matrix.lookAt(splineCamera.position, lookAt, normal);
-			splineCamera.rotation.setEulerFromRotationMatrix( splineCamera.matrix, splineCamera.eulerOrder );
+			splineCamera.rotation.setFromRotationMatrix( splineCamera.matrix, splineCamera.rotation.order );
 
 			cameraHelper.update();
 

+ 1 - 1
examples/webgl_marching_cubes.html

@@ -1,4 +1,4 @@
-<!DOCTYPE HTML>
+<!DOCTYPE html>
 <html lang="en">
 <head>
 	<title>three.js webgl - marching cubes</title>

+ 1 - 1
examples/webgl_multiple_windows.html

@@ -192,7 +192,7 @@
 					dst.up.copy( src.up );
 					dst.position.copy( src.position );
 					dst.scale.copy( src.scale );
-					dst.eulerOrder = src.eulerOrder;
+					dst.rotation.copy( src.rotation );
 					dst.quaternion.copy( src.quaternion );
 
 				};

+ 1 - 9
src/cameras/Camera.js

@@ -27,15 +27,7 @@ THREE.Camera.prototype.lookAt = function () {
 
 		m1.lookAt( this.position, vector, this.up );
 
-		if ( this.useQuaternion === true )  {
-
-			this.quaternion.setFromRotationMatrix( m1 );
-
-		} else {
-
-			this.rotation.setEulerFromRotationMatrix( m1, this.eulerOrder );
-
-		}
+		this.quaternion.setFromRotationMatrix( m1 );
 
 	};
 

+ 11 - 60
src/core/Object3D.js

@@ -17,10 +17,12 @@ THREE.Object3D = function () {
 	this.up = new THREE.Vector3( 0, 1, 0 );
 
 	this.position = new THREE.Vector3();
-	this.rotation = new THREE.Vector3();
-	this.eulerOrder = THREE.Object3D.defaultEulerOrder;
+	this.quaternion = new THREE.Quaternion();
 	this.scale = new THREE.Vector3( 1, 1, 1 );
 
+	// for backwards compatibility (maps changes to this.rotation onto this.quaternion)
+	this.rotation = new THREE.Rotation( this.quaternion );
+
 	this.renderDepth = null;
 
 	this.rotationAutoUpdate = true;
@@ -31,9 +33,6 @@ THREE.Object3D = function () {
 	this.matrixAutoUpdate = true;
 	this.matrixWorldNeedsUpdate = true;
 
-	this.quaternion = new THREE.Quaternion();
-	this.useQuaternion = false;
-
 	this.visible = true;
 
 	this.castShadow = false;
@@ -69,15 +68,7 @@ THREE.Object3D.prototype = {
 
 			m1.extractRotation( this.matrix );
 
-			if ( this.useQuaternion === true )  {
-
-				this.quaternion.setFromRotationMatrix( m1 );
-
-			} else {
-
-				this.rotation.setEulerFromRotationMatrix( m1, this.eulerOrder );
-
-			}
+			this.quaternion.setFromRotationMatrix( m1 );
 
 		}
 
@@ -95,18 +86,7 @@ THREE.Object3D.prototype = {
 
 			q1.setFromAxisAngle( axis, angle );
 
-			if ( this.useQuaternion === true ) {
-
-				this.quaternion.multiply( q1 );
-
-			} else {
-
-				q2.setFromEuler( this.rotation, this.eulerOrder );
-				q2.multiply( q1 );
-
-				this.rotation.setEulerFromQuaternion( q2, this.eulerOrder );
-
-			}
+			this.quaternion.multiply( q1 );
 
 			return this;
 
@@ -125,15 +105,7 @@ THREE.Object3D.prototype = {
 
 			v1.copy( axis );
 
-			if ( this.useQuaternion === true ) {
-
-				v1.applyQuaternion( this.quaternion );
-
-			} else {
-
-				v1.applyEuler( this.rotation, this.eulerOrder );
-
-			}
+			v1.applyQuaternion( this.quaternion );
 
 			this.position.add( v1.multiplyScalar( distance ) );
 
@@ -214,15 +186,7 @@ THREE.Object3D.prototype = {
 
 			m1.lookAt( vector, this.position, this.up );
 
-			if ( this.useQuaternion === true )  {
-
-				this.quaternion.setFromRotationMatrix( m1 );
-
-			} else {
-
-				this.rotation.setEulerFromRotationMatrix( m1, this.eulerOrder );
-
-			}
+			this.quaternion.setFromRotationMatrix( m1 );
 
 		};
 
@@ -394,17 +358,7 @@ THREE.Object3D.prototype = {
 
 	updateMatrix: function () {
 
-		// if we are not using a quaternion directly, convert Euler rotation to this.quaternion.
-
-		if ( this.useQuaternion === false )  {
-
-			this.matrix.makeFromPositionEulerScale( this.position, this.rotation, this.eulerOrder, this.scale );
-
-		} else {
-
-			this.matrix.makeFromPositionQuaternionScale( this.position, this.quaternion, this.scale );
-
-		}
+		this.matrix.makeFromPositionQuaternionScale( this.position, this.quaternion, this.scale );
 
 		this.matrixWorldNeedsUpdate = true;
 
@@ -452,8 +406,8 @@ THREE.Object3D.prototype = {
 		object.up.copy( this.up );
 
 		object.position.copy( this.position );
-		if ( object.rotation instanceof THREE.Vector3 ) object.rotation.copy( this.rotation ); // because of Sprite madness
-		object.eulerOrder = this.eulerOrder;
+		object.quaternion.copy( this.quaternion );
+		object.rotation = new THREE.Rotation( object.quaternion );		
 		object.scale.copy( this.scale );
 
 		object.renderDepth = this.renderDepth;
@@ -466,9 +420,6 @@ THREE.Object3D.prototype = {
 		object.matrixAutoUpdate = this.matrixAutoUpdate;
 		object.matrixWorldNeedsUpdate = this.matrixWorldNeedsUpdate;
 
-		object.quaternion.copy( this.quaternion );
-		object.useQuaternion = this.useQuaternion;
-
 		object.visible = this.visible;
 
 		object.castShadow = this.castShadow;

+ 0 - 6
src/extras/animation/Animation.js

@@ -41,12 +41,6 @@ THREE.Animation.prototype.play = function ( loop, startTimeMS ) {
 
 			object = this.hierarchy[ h ];
 
-			if ( this.interpolationType !== THREE.AnimationHandler.CATMULLROM_FORWARD ) {
-
-				object.useQuaternion = true;
-
-			}
-
 			object.matrixAutoUpdate = true;
 
 			if ( object.animationCache === undefined ) {

+ 0 - 1
src/extras/animation/KeyFrameAnimation.js

@@ -75,7 +75,6 @@ THREE.KeyFrameAnimation.prototype.play = function( loop, startTimeMS ) {
 
 			object = this.hierarchy[ h ];
 			node = this.data.hierarchy[ h ];
-			object.useQuaternion = true;
 
 			if ( node.animationCache === undefined ) {
 

+ 0 - 2
src/extras/helpers/ArrowHelper.js

@@ -23,8 +23,6 @@ THREE.ArrowHelper = function ( dir, origin, length, hex ) {
 
 	this.position = origin;
 
-	this.useQuaternion = true;
-
 	var lineGeometry = new THREE.Geometry();
 	lineGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ) );
 	lineGeometry.vertices.push( new THREE.Vector3( 0, 1, 0 ) );

+ 289 - 0
src/math/Euler.js

@@ -0,0 +1,289 @@
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author WestLangley / http://github.com/WestLangley
+ * @author bhouston / http://exocortex.com
+ */
+
+THREE.Euler = function ( x, y, z, order ) {
+
+	this.x = x || 0;
+	this.y = y || 0;
+	this.z = z || 0;
+	this.order = order || THREE.Euler.DefaultOrder;
+
+};
+
+THREE.Euler.RotationOrders = [ 'XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX' ];
+
+THREE.Euler.DefaultOrder = 'XYZ';
+
+THREE.Euler.prototype = {
+
+	constructor: THREE.Euler,
+
+	set: function ( x, y, z, order ) {
+
+		this.x = x;
+		this.y = y;
+		this.z = z;
+		this.order = order || this.order;
+
+		return this;
+
+	},
+
+	copy: function ( e ) {
+
+		this.x = e.x;
+		this.y = e.y;
+		this.z = e.z;
+		this.order = e.order;
+
+		return this;
+
+	},
+
+	setFromRotationMatrix: function ( m, order ) {
+
+		// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
+
+		// clamp, to handle numerical problems
+
+		function clamp( x ) {
+
+			return Math.min( Math.max( x, -1 ), 1 );
+
+		}
+
+		var te = m.elements;
+		var m11 = te[0], m12 = te[4], m13 = te[8];
+		var m21 = te[1], m22 = te[5], m23 = te[9];
+		var m31 = te[2], m32 = te[6], m33 = te[10];
+
+		order = order || this.order;
+
+		if ( order === 'XYZ' ) {
+
+			this.y = Math.asin( clamp( m13 ) );
+
+			if ( Math.abs( m13 ) < 0.99999 ) {
+
+				this.x = Math.atan2( - m23, m33 );
+				this.z = Math.atan2( - m12, m11 );
+
+			} else {
+
+				this.x = Math.atan2( m32, m22 );
+				this.z = 0;
+
+			}
+
+		} else if ( order === 'YXZ' ) {
+
+			this.x = Math.asin( - clamp( m23 ) );
+
+			if ( Math.abs( m23 ) < 0.99999 ) {
+
+				this.y = Math.atan2( m13, m33 );
+				this.z = Math.atan2( m21, m22 );
+
+			} else {
+
+				this.y = Math.atan2( - m31, m11 );
+				this.z = 0;
+
+			}
+
+		} else if ( order === 'ZXY' ) {
+
+			this.x = Math.asin( clamp( m32 ) );
+
+			if ( Math.abs( m32 ) < 0.99999 ) {
+
+				this.y = Math.atan2( - m31, m33 );
+				this.z = Math.atan2( - m12, m22 );
+
+			} else {
+
+				this.y = 0;
+				this.z = Math.atan2( m21, m11 );
+
+			}
+
+		} else if ( order === 'ZYX' ) {
+
+			this.y = Math.asin( - clamp( m31 ) );
+
+			if ( Math.abs( m31 ) < 0.99999 ) {
+
+				this.x = Math.atan2( m32, m33 );
+				this.z = Math.atan2( m21, m11 );
+
+			} else {
+
+				this.x = 0;
+				this.z = Math.atan2( - m12, m22 );
+
+			}
+
+		} else if ( order === 'YZX' ) {
+
+			this.z = Math.asin( clamp( m21 ) );
+
+			if ( Math.abs( m21 ) < 0.99999 ) {
+
+				this.x = Math.atan2( - m23, m22 );
+				this.y = Math.atan2( - m31, m11 );
+
+			} else {
+
+				this.x = 0;
+				this.y = Math.atan2( m13, m33 );
+
+			}
+
+		} else if ( order === 'XZY' ) {
+
+			this.z = Math.asin( - clamp( m12 ) );
+
+			if ( Math.abs( m12 ) < 0.99999 ) {
+
+				this.x = Math.atan2( m32, m22 );
+				this.y = Math.atan2( m13, m11 );
+
+			} else {
+
+				this.x = Math.atan2( - m23, m33 );
+				this.y = 0;
+
+			}
+
+		}
+		else {
+
+			console.warn( 'WARNING: Euler.setFromRotationMatrix() given unsupported order: ' + order )
+		
+		}
+
+		this.order = order;
+
+		return this;
+
+	},
+
+	setFromQuaternion: function ( q, order ) {
+
+		// q is assumed to be normalized
+
+		// clamp, to handle numerical problems
+
+		function clamp( x ) {
+
+			return Math.min( Math.max( x, -1 ), 1 );
+
+		}
+
+		// http://www.mathworks.com/matlabcentral/fileexchange/20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/content/SpinCalc.m
+
+		var sqx = q.x * q.x;
+		var sqy = q.y * q.y;
+		var sqz = q.z * q.z;
+		var sqw = q.w * q.w;
+
+		order = order || this.order;
+
+		if ( order === 'XYZ' ) {
+
+			this.x = Math.atan2( 2 * ( q.x * q.w - q.y * q.z ), ( sqw - sqx - sqy + sqz ) );
+			this.y = Math.asin(  clamp( 2 * ( q.x * q.z + q.y * q.w ) ) );
+			this.z = Math.atan2( 2 * ( q.z * q.w - q.x * q.y ), ( sqw + sqx - sqy - sqz ) );
+
+		} else if ( order ===  'YXZ' ) {
+
+			this.x = Math.asin(  clamp( 2 * ( q.x * q.w - q.y * q.z ) ) );
+			this.y = Math.atan2( 2 * ( q.x * q.z + q.y * q.w ), ( sqw - sqx - sqy + sqz ) );
+			this.z = Math.atan2( 2 * ( q.x * q.y + q.z * q.w ), ( sqw - sqx + sqy - sqz ) );
+
+		} else if ( order === 'ZXY' ) {
+
+			this.x = Math.asin(  clamp( 2 * ( q.x * q.w + q.y * q.z ) ) );
+			this.y = Math.atan2( 2 * ( q.y * q.w - q.z * q.x ), ( sqw - sqx - sqy + sqz ) );
+			this.z = Math.atan2( 2 * ( q.z * q.w - q.x * q.y ), ( sqw - sqx + sqy - sqz ) );
+
+		} else if ( order === 'ZYX' ) {
+
+			this.x = Math.atan2( 2 * ( q.x * q.w + q.z * q.y ), ( sqw - sqx - sqy + sqz ) );
+			this.y = Math.asin(  clamp( 2 * ( q.y * q.w - q.x * q.z ) ) );
+			this.z = Math.atan2( 2 * ( q.x * q.y + q.z * q.w ), ( sqw + sqx - sqy - sqz ) );
+
+		} else if ( order === 'YZX' ) {
+
+			this.x = Math.atan2( 2 * ( q.x * q.w - q.z * q.y ), ( sqw - sqx + sqy - sqz ) );
+			this.y = Math.atan2( 2 * ( q.y * q.w - q.x * q.z ), ( sqw + sqx - sqy - sqz ) );
+			this.z = Math.asin(  clamp( 2 * ( q.x * q.y + q.z * q.w ) ) );
+
+		} else if ( order === 'XZY' ) {
+
+			this.x = Math.atan2( 2 * ( q.x * q.w + q.y * q.z ), ( sqw - sqx + sqy - sqz ) );
+			this.y = Math.atan2( 2 * ( q.x * q.z + q.y * q.w ), ( sqw + sqx - sqy - sqz ) );
+			this.z = Math.asin(  clamp( 2 * ( q.z * q.w - q.x * q.y ) ) );
+
+		}
+		else {
+
+			console.warn( 'WARNING: Euler.setFromQuaternion() given unsupported order: ' + order )
+		
+		}
+
+		this.order = order;
+
+		return this;
+
+	},
+
+	reorder: function() {
+
+		// WARNING: this discards revolution information -bhouston
+
+		var q = new THREE.Quaternion();
+
+		return function( newOrder ) {
+
+			q.setFromEuler( this );
+			this.setFromQuaternion( q, newOrder );
+
+		};
+
+
+	}(),
+
+	fromArray: function ( array ) {
+
+		this.x = array[ 0 ];
+		this.y = array[ 1 ];
+		this.z = array[ 2 ];
+		this.order = array[ 3 ];
+
+		return this;
+
+	},
+
+	toArray: function () {
+
+		return [ this.x, this.y, this.z, this.order ];
+
+	},
+
+	equals: function ( rotation ) {
+
+		return ( ( rotation.x === this.x ) && ( rotation.y === this.y ) && ( rotation.z === this.z ) && ( rotation.order === this.order ) );
+
+	},
+
+	clone: function () {
+
+		return new THREE.Euler( this.x, this.y, this.z, this.order );
+
+	}
+
+};

+ 17 - 17
src/math/Matrix4.js

@@ -126,24 +126,20 @@ THREE.Matrix4.prototype = {
 
 	}(),
 
-	setRotationFromEuler: function ( v, order ) {
+	makeRotationFromEuler: function ( rotation ) {
 
-		console.warn( 'DEPRECATED: Matrix4\'s .setRotationFromEuler() has been deprecated in favor of makeRotationFromEuler.  Please update your code.' );
-
-		return this.makeRotationFromEuler( v, order );
-
-	},
-
-	makeRotationFromEuler: function ( v, order ) {
+		if( typeof rotation['order'] === undefined ) {
+			console.error( 'ERROR: Matrix\'s .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.  Please update your code.' );
+		}
 
 		var te = this.elements;
 
-		var x = v.x, y = v.y, z = v.z;
+		var x = rotation.x, y = rotation.y, z = rotation.z;
 		var a = Math.cos( x ), b = Math.sin( x );
 		var c = Math.cos( y ), d = Math.sin( y );
 		var e = Math.cos( z ), f = Math.sin( z );
 
-		if ( order === undefined || order === 'XYZ' ) {
+		if ( rotation.order === undefined || rotation.order === 'XYZ' ) {
 
 			var ae = a * e, af = a * f, be = b * e, bf = b * f;
 
@@ -159,7 +155,7 @@ THREE.Matrix4.prototype = {
 			te[6] = be + af * d;
 			te[10] = a * c;
 
-		} else if ( order === 'YXZ' ) {
+		} else if ( rotation.order === 'YXZ' ) {
 
 			var ce = c * e, cf = c * f, de = d * e, df = d * f;
 
@@ -175,7 +171,7 @@ THREE.Matrix4.prototype = {
 			te[6] = df + ce * b;
 			te[10] = a * c;
 
-		} else if ( order === 'ZXY' ) {
+		} else if ( rotation.order === 'ZXY' ) {
 
 			var ce = c * e, cf = c * f, de = d * e, df = d * f;
 
@@ -191,7 +187,7 @@ THREE.Matrix4.prototype = {
 			te[6] = b;
 			te[10] = a * c;
 
-		} else if ( order === 'ZYX' ) {
+		} else if ( rotation.order === 'ZYX' ) {
 
 			var ae = a * e, af = a * f, be = b * e, bf = b * f;
 
@@ -207,7 +203,7 @@ THREE.Matrix4.prototype = {
 			te[6] = b * c;
 			te[10] = a * c;
 
-		} else if ( order === 'YZX' ) {
+		} else if ( rotation.order === 'YZX' ) {
 
 			var ac = a * c, ad = a * d, bc = b * c, bd = b * d;
 
@@ -223,7 +219,7 @@ THREE.Matrix4.prototype = {
 			te[6] = ad * f + bc;
 			te[10] = ac - bd * f;
 
-		} else if ( order === 'XZY' ) {
+		} else if ( rotation.order === 'XZY' ) {
 
 			var ac = a * c, ad = a * d, bc = b * c, bd = b * d;
 
@@ -843,9 +839,13 @@ THREE.Matrix4.prototype = {
 
 	},
 
-	makeFromPositionEulerScale: function ( position, rotation, eulerOrder, scale ) {
+	makeFromPositionEulerScale: function ( position, rotation, scale ) {
+
+		if( typeof rotation['order'] === undefined ) {
+			console.error( 'ERROR: Matrix4\'s .makeFromPositionEulerScale() now expects a Euler rotation rather than a Vector3 and order.  Please update your code.' );
+		}
 
-		this.makeRotationFromEuler( rotation, eulerOrder );
+		this.makeRotationFromEuler( rotation );
 		this.scale( scale );
 		this.setPosition( position );
 

+ 17 - 13
src/math/Quaternion.js

@@ -40,55 +40,59 @@ THREE.Quaternion.prototype = {
 
 	},
 
-	setFromEuler: function ( v, order ) {
+	setFromEuler: function ( rotation ) {
+
+		if( typeof rotation['order'] === undefined ) {
+			console.error( 'ERROR: Quaternion\'s .setFromEuler() now expects a Euler rotation rather than a Vector3 and order.  Please update your code.' );
+		}
 
 		// http://www.mathworks.com/matlabcentral/fileexchange/
 		// 	20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/
 		//	content/SpinCalc.m
 
-		var c1 = Math.cos( v.x / 2 );
-		var c2 = Math.cos( v.y / 2 );
-		var c3 = Math.cos( v.z / 2 );
-		var s1 = Math.sin( v.x / 2 );
-		var s2 = Math.sin( v.y / 2 );
-		var s3 = Math.sin( v.z / 2 );
+		var c1 = Math.cos( rotation.x / 2 );
+		var c2 = Math.cos( rotation.y / 2 );
+		var c3 = Math.cos( rotation.z / 2 );
+		var s1 = Math.sin( rotation.x / 2 );
+		var s2 = Math.sin( rotation.y / 2 );
+		var s3 = Math.sin( rotation.z / 2 );
 
-		if ( order === undefined || order === 'XYZ' ) {
+		if ( rotation.order === undefined || rotation.order === 'XYZ' ) {
 
 			this.x = s1 * c2 * c3 + c1 * s2 * s3;
 			this.y = c1 * s2 * c3 - s1 * c2 * s3;
 			this.z = c1 * c2 * s3 + s1 * s2 * c3;
 			this.w = c1 * c2 * c3 - s1 * s2 * s3;
 
-		} else if ( order === 'YXZ' ) {
+		} else if ( rotation.order === 'YXZ' ) {
 
 			this.x = s1 * c2 * c3 + c1 * s2 * s3;
 			this.y = c1 * s2 * c3 - s1 * c2 * s3;
 			this.z = c1 * c2 * s3 - s1 * s2 * c3;
 			this.w = c1 * c2 * c3 + s1 * s2 * s3;
 
-		} else if ( order === 'ZXY' ) {
+		} else if ( rotation.order === 'ZXY' ) {
 
 			this.x = s1 * c2 * c3 - c1 * s2 * s3;
 			this.y = c1 * s2 * c3 + s1 * c2 * s3;
 			this.z = c1 * c2 * s3 + s1 * s2 * c3;
 			this.w = c1 * c2 * c3 - s1 * s2 * s3;
 
-		} else if ( order === 'ZYX' ) {
+		} else if ( rotation.order === 'ZYX' ) {
 
 			this.x = s1 * c2 * c3 - c1 * s2 * s3;
 			this.y = c1 * s2 * c3 + s1 * c2 * s3;
 			this.z = c1 * c2 * s3 - s1 * s2 * c3;
 			this.w = c1 * c2 * c3 + s1 * s2 * s3;
 
-		} else if ( order === 'YZX' ) {
+		} else if ( rotation.order === 'YZX' ) {
 
 			this.x = s1 * c2 * c3 + c1 * s2 * s3;
 			this.y = c1 * s2 * c3 + s1 * c2 * s3;
 			this.z = c1 * c2 * s3 - s1 * s2 * c3;
 			this.w = c1 * c2 * c3 - s1 * s2 * s3;
 
-		} else if ( order === 'XZY' ) {
+		} else if ( rotation.order === 'XZY' ) {
 
 			this.x = s1 * c2 * c3 - c1 * s2 * s3;
 			this.y = c1 * s2 * c3 - s1 * c2 * s3;

+ 83 - 0
src/math/Rotation.js

@@ -0,0 +1,83 @@
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author bhouston / http://exocortex.com/
+  */
+
+THREE.Rotation = function ( quaternion ) {
+
+    this.euler = new THREE.Euler();
+    this.quaternion = quaternion;
+
+};
+
+THREE.Rotation.prototype = {
+
+    get x () {
+
+        return this.euler.x;
+
+    },
+
+    set x ( value ) {
+
+        this.euler.x = value;
+        this.quaternion.setFromEuler( this.euler );
+
+    },
+
+    get y () {
+
+        return this.euler.y;
+
+    },
+
+    set y ( value ) {
+
+        this.euler.y = value;
+        this.quaternion.setFromEuler( this.euler );
+
+    },
+
+    get z () {
+
+        return this.euler.z;
+
+    },
+
+    set z ( value ) {
+
+        this.euler.z = value;
+        this.quaternion.setFromEuler( this.euler );
+
+    },
+
+    set: function ( x, y, z ) {
+
+        this.euler.x = x;
+        this.euler.y = y;
+        this.euler.z = z;
+
+        this.quaternion.setFromEuler( this.euler );
+
+    },
+  
+    copy: function ( rotation ) {
+
+        this.quaternion.copy( rotation.quaternion );
+
+    },
+
+    fromArray: function( array ) {
+
+        this.euler.fromArray( array );
+        this.quaternion.setFromEuler( this.euler );
+
+    },
+
+    toArray: function () {
+
+        return this.euler.toArray();
+
+    }
+
+};

+ 9 - 173
src/math/Vector3.js

@@ -518,182 +518,14 @@ THREE.Vector3.prototype = {
 
 	setEulerFromRotationMatrix: function ( m, order ) {
 
-		// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
-
-		// clamp, to handle numerical problems
-
-		function clamp( x ) {
-
-			return Math.min( Math.max( x, -1 ), 1 );
-
-		}
-
-		var te = m.elements;
-		var m11 = te[0], m12 = te[4], m13 = te[8];
-		var m21 = te[1], m22 = te[5], m23 = te[9];
-		var m31 = te[2], m32 = te[6], m33 = te[10];
-
-		if ( order === undefined || order === 'XYZ' ) {
-
-			this.y = Math.asin( clamp( m13 ) );
-
-			if ( Math.abs( m13 ) < 0.99999 ) {
-
-				this.x = Math.atan2( - m23, m33 );
-				this.z = Math.atan2( - m12, m11 );
-
-			} else {
-
-				this.x = Math.atan2( m32, m22 );
-				this.z = 0;
-
-			}
-
-		} else if ( order === 'YXZ' ) {
-
-			this.x = Math.asin( - clamp( m23 ) );
-
-			if ( Math.abs( m23 ) < 0.99999 ) {
-
-				this.y = Math.atan2( m13, m33 );
-				this.z = Math.atan2( m21, m22 );
-
-			} else {
-
-				this.y = Math.atan2( - m31, m11 );
-				this.z = 0;
-
-			}
-
-		} else if ( order === 'ZXY' ) {
-
-			this.x = Math.asin( clamp( m32 ) );
-
-			if ( Math.abs( m32 ) < 0.99999 ) {
-
-				this.y = Math.atan2( - m31, m33 );
-				this.z = Math.atan2( - m12, m22 );
-
-			} else {
-
-				this.y = 0;
-				this.z = Math.atan2( m21, m11 );
-
-			}
-
-		} else if ( order === 'ZYX' ) {
-
-			this.y = Math.asin( - clamp( m31 ) );
-
-			if ( Math.abs( m31 ) < 0.99999 ) {
-
-				this.x = Math.atan2( m32, m33 );
-				this.z = Math.atan2( m21, m11 );
-
-			} else {
-
-				this.x = 0;
-				this.z = Math.atan2( - m12, m22 );
-
-			}
-
-		} else if ( order === 'YZX' ) {
-
-			this.z = Math.asin( clamp( m21 ) );
-
-			if ( Math.abs( m21 ) < 0.99999 ) {
-
-				this.x = Math.atan2( - m23, m22 );
-				this.y = Math.atan2( - m31, m11 );
-
-			} else {
-
-				this.x = 0;
-				this.y = Math.atan2( m13, m33 );
-
-			}
-
-		} else if ( order === 'XZY' ) {
-
-			this.z = Math.asin( - clamp( m12 ) );
-
-			if ( Math.abs( m12 ) < 0.99999 ) {
-
-				this.x = Math.atan2( m32, m22 );
-				this.y = Math.atan2( m13, m11 );
-
-			} else {
-
-				this.x = Math.atan2( - m23, m33 );
-				this.y = 0;
-
-			}
-
-		}
-
-		return this;
+		console.error( "REMOVED: Vector3\'s setEulerFromRotationMatrix has been removed in favor of Euler.setFromRotationMatrix(), please update your code.");
 
 	},
 
 	setEulerFromQuaternion: function ( q, order ) {
 
-		// q is assumed to be normalized
-
-		// clamp, to handle numerical problems
-
-		function clamp( x ) {
-
-			return Math.min( Math.max( x, -1 ), 1 );
-
-		}
-
-		// http://www.mathworks.com/matlabcentral/fileexchange/20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/content/SpinCalc.m
-
-		var sqx = q.x * q.x;
-		var sqy = q.y * q.y;
-		var sqz = q.z * q.z;
-		var sqw = q.w * q.w;
-
-		if ( order === undefined || order === 'XYZ' ) {
-
-			this.x = Math.atan2( 2 * ( q.x * q.w - q.y * q.z ), ( sqw - sqx - sqy + sqz ) );
-			this.y = Math.asin(  clamp( 2 * ( q.x * q.z + q.y * q.w ) ) );
-			this.z = Math.atan2( 2 * ( q.z * q.w - q.x * q.y ), ( sqw + sqx - sqy - sqz ) );
-
-		} else if ( order ===  'YXZ' ) {
-
-			this.x = Math.asin(  clamp( 2 * ( q.x * q.w - q.y * q.z ) ) );
-			this.y = Math.atan2( 2 * ( q.x * q.z + q.y * q.w ), ( sqw - sqx - sqy + sqz ) );
-			this.z = Math.atan2( 2 * ( q.x * q.y + q.z * q.w ), ( sqw - sqx + sqy - sqz ) );
-
-		} else if ( order === 'ZXY' ) {
-
-			this.x = Math.asin(  clamp( 2 * ( q.x * q.w + q.y * q.z ) ) );
-			this.y = Math.atan2( 2 * ( q.y * q.w - q.z * q.x ), ( sqw - sqx - sqy + sqz ) );
-			this.z = Math.atan2( 2 * ( q.z * q.w - q.x * q.y ), ( sqw - sqx + sqy - sqz ) );
-
-		} else if ( order === 'ZYX' ) {
-
-			this.x = Math.atan2( 2 * ( q.x * q.w + q.z * q.y ), ( sqw - sqx - sqy + sqz ) );
-			this.y = Math.asin(  clamp( 2 * ( q.y * q.w - q.x * q.z ) ) );
-			this.z = Math.atan2( 2 * ( q.x * q.y + q.z * q.w ), ( sqw + sqx - sqy - sqz ) );
-
-		} else if ( order === 'YZX' ) {
-
-			this.x = Math.atan2( 2 * ( q.x * q.w - q.z * q.y ), ( sqw - sqx + sqy - sqz ) );
-			this.y = Math.atan2( 2 * ( q.y * q.w - q.x * q.z ), ( sqw + sqx - sqy - sqz ) );
-			this.z = Math.asin(  clamp( 2 * ( q.x * q.y + q.z * q.w ) ) );
-
-		} else if ( order === 'XZY' ) {
-
-			this.x = Math.atan2( 2 * ( q.x * q.w + q.y * q.z ), ( sqw - sqx + sqy - sqz ) );
-			this.y = Math.atan2( 2 * ( q.x * q.z + q.y * q.w ), ( sqw + sqx - sqy - sqz ) );
-			this.z = Math.asin(  clamp( 2 * ( q.z * q.w - q.x * q.y ) ) );
-
-		}
-
-		return this;
-
+		console.error( "REMOVED: Vector3\'s setEulerFromQuaternion: has been removed in favor of Euler.setFromQuaternion(), please update your code.");
+		
 	},
 
 	getPositionFromMatrix: function ( m ) {
@@ -769,9 +601,13 @@ THREE.extend( THREE.Vector3.prototype, {
 
 		var q1 = new THREE.Quaternion();
 
-		return function ( v, eulerOrder ) {
+		return function ( rotation ) {
+
+			if( typeof rotation['order'] === undefined ) {
+				console.error( 'ERROR: Vector3\'s .applyEuler() now expects a Euler rotation rather than a Vector3 and order.  Please update your code.' );
+			}
 
-			var quaternion = q1.setFromEuler( v, eulerOrder );
+			var quaternion = q1.setFromEuler( rotation );
 
 			this.applyQuaternion( quaternion );
 

+ 1 - 2
src/objects/SkinnedMesh.js

@@ -35,8 +35,7 @@ THREE.SkinnedMesh = function ( geometry, material, useVertexTexture ) {
 			bone.name = gbone.name;
 			bone.position.set( p[0], p[1], p[2] );
 			bone.quaternion.set( q[0], q[1], q[2], q[3] );
-			bone.useQuaternion = true;
-
+		
 			if ( s !== undefined ) {
 
 				bone.scale.set( s[0], s[1], s[2] );

+ 2 - 2
src/objects/Sprite.js

@@ -22,8 +22,8 @@ THREE.Sprite.prototype = Object.create( THREE.Object3D.prototype );
 
 THREE.Sprite.prototype.updateMatrix = function () {
 
-	this.rotation3d.set( 0, 0, this.rotation );
-	this.quaternion.setFromEuler( this.rotation3d, this.eulerOrder );
+	this.rotation3d.set( 0, 0, this.rotation, this.rotation3d.order );
+	this.quaternion.setFromEuler( this.rotation3d );
 	this.matrix.makeFromPositionQuaternionScale( this.position, this.quaternion, this.scale );
 
 	this.matrixWorldNeedsUpdate = true;

+ 93 - 0
test/unit/math/Euler.js

@@ -0,0 +1,93 @@
+/**
+ * @author bhouston / http://exocortex.com
+ */
+
+module( "Euler" );
+
+var eulerZero = new THREE.Euler( 0, 0, 0, "XYZ" );
+var eulerAxyz = new THREE.Euler( 1, 0, 0, "XYZ" );
+var eulerAzyx = new THREE.Euler( 0, 1, 0, "ZYX" );
+	
+var matrixEquals4 = function( a, b, tolerance ) {
+	tolerance = tolerance || 0.0001;
+	if( a.elements.length != b.elements.length ) {
+		return false;
+	}
+	for( var i = 0, il = a.elements.length; i < il; i ++ ) {
+		var delta = a.elements[i] - b.elements[i];
+		if( delta > tolerance ) {
+			return false;
+		}
+	}
+	return true;
+};
+
+test( "constructor/equals", function() {
+	var a = new THREE.Euler();
+	ok( a.equals( eulerZero ), "Passed!" );
+	ok( ! a.equals( eulerAxyz ), "Passed!" );
+	ok( ! a.equals( eulerAzyx ), "Passed!" );
+});
+
+test( "clone/copy/equals", function() {
+	var a = eulerAxyz.clone();
+	ok( a.equals( eulerAxyz ), "Passed!" );
+	ok( ! a.equals( eulerZero ), "Passed!" );
+	ok( ! a.equals( eulerAzyx ), "Passed!" );
+
+	a.copy( eulerAzyx );
+	ok( a.equals( eulerAzyx ), "Passed!" );
+	ok( ! a.equals( eulerAxyz ), "Passed!" );
+	ok( ! a.equals( eulerZero ), "Passed!" );
+
+});
+
+test( "set", function() {
+	var a = new THREE.Euler();
+
+	a.set( 0, 1, 0, "ZYX" );
+	ok( a.equals( eulerAzyx ), "Passed!" );
+	ok( ! a.equals( eulerAxyz ), "Passed!" );
+	ok( ! a.equals( eulerZero ), "Passed!" );
+});
+
+test( "Quaternion.setFromEuler/Euler.fromQuaternion", function() {
+	var testValues = [ eulerZero, eulerAxyz, eulerAzyx ];
+	for( var i = 0; i < testValues.length; i ++ ) {
+		var v = testValues[i];
+		var q = new THREE.Quaternion().setFromEuler( v );
+
+		var v2 = new THREE.Euler().setFromQuaternion( q, v.order );
+		var q2 = new THREE.Quaternion().setFromEuler( v2 );
+		ok( q.equals( q2 ), "Passed!" );	
+	}
+});
+
+
+test( "Matrix4.setFromEuler/Euler.fromRotationMatrix", function() {
+	var testValues = [ eulerZero, eulerAxyz, eulerAzyx ];
+	for( var i = 0; i < testValues.length; i ++ ) {
+		var v = testValues[i];
+		var m = new THREE.Matrix4().makeRotationFromEuler( v );
+
+		var v2 = new THREE.Euler().setFromRotationMatrix( m, v.order );
+		var m2 = new THREE.Matrix4().makeRotationFromEuler( v2 );
+		ok( matrixEquals4( m, m2, 0.0001 ), "Passed!" );	
+	}
+});
+
+test( "reorder", function() {
+	var testValues = [ eulerZero, eulerAxyz, eulerAzyx ];
+	for( var i = 0; i < testValues.length; i ++ ) {
+		var v = testValues[i];
+		var q = new THREE.Quaternion().setFromEuler( v );
+
+		v.reorder( 'YZX' );		
+		var q2 = new THREE.Quaternion().setFromEuler( v );
+		ok( q.equals( q2 ), "Passed!" );	
+
+		v.reorder( 'ZXY' );
+		var q3 = new THREE.Quaternion().setFromEuler( v );
+		ok( q.equals( q3 ), "Passed!" );	
+	}
+});

+ 4 - 4
test/unit/math/Quaternion.js

@@ -91,16 +91,16 @@ test( "setFromAxisAngle", function() {
 });
 
 
-test( "setFromEuler/setEulerFromQuaternion", function() {
+test( "setFromEuler/setFromQuaternion", function() {
 
 	var angles = [ new THREE.Vector3( 1, 0, 0 ), new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, 0, 1 ) ];
 
 	// ensure euler conversion to/from Quaternion matches.
 	for( var i = 0; i < orders.length; i ++ ) {
 		for( var j = 0; j < angles.length; j ++ ) {
-			var eulers2 = new THREE.Vector3().setEulerFromQuaternion( new THREE.Quaternion().setFromEuler( angles[j], orders[i] ), orders[i] );
-
-			ok( eulers2.distanceTo( angles[j] ) < 0.001, "Passed!" );
+			var eulers2 = new THREE.Euler().setFromQuaternion( new THREE.Quaternion().setFromEuler( new THREE.Euler( angles[j].x, angles[j].y, angles[j].z, orders[i] ) ), orders[i] );
+			var newAngle = new THREE.Vector3( eulers2.x, eulers2.y, eulers2.z );
+			ok( newAngle.distanceTo( angles[j] ) < 0.001, "Passed!" );
 		}
 	}
 

+ 2 - 0
test/unit/unittests_sources.html

@@ -17,6 +17,7 @@
   <script src="../../src/math/Vector2.js"></script>
   <script src="../../src/math/Vector3.js"></script>
   <script src="../../src/math/Vector4.js"></script>
+  <script src="../../src/math/Euler.js"></script>
   <script src="../../src/math/Line3.js"></script>
   <script src="../../src/math/Box2.js"></script>
   <script src="../../src/math/Box3.js"></script>
@@ -41,6 +42,7 @@
   <script src="math/Vector2.js"></script>
   <script src="math/Vector3.js"></script>
   <script src="math/Vector4.js"></script>
+  <script src="math/Euler.js"></script>
   <script src="math/Line3.js"></script>
   <script src="math/Quaternion.js"></script>
   <script src="math/Matrix3.js"></script>

+ 1 - 0
test/unit/unittests_three-math.html

@@ -25,6 +25,7 @@
   <script src="math/Vector2.js"></script>
   <script src="math/Vector3.js"></script>
   <script src="math/Vector4.js"></script>
+  <script src="math/Euler.js"></script>
   <script src="math/Line3.js"></script>
   <script src="math/Quaternion.js"></script>
   <script src="math/Matrix3.js"></script>

+ 1 - 0
test/unit/unittests_three.html

@@ -25,6 +25,7 @@
   <script src="math/Vector2.js"></script>
   <script src="math/Vector3.js"></script>
   <script src="math/Vector4.js"></script>
+  <script src="math/Euler.js"></script>
   <script src="math/Line3.js"></script>
   <script src="math/Quaternion.js"></script>
   <script src="math/Matrix3.js"></script>

+ 1 - 0
test/unit/unittests_three.min.html

@@ -25,6 +25,7 @@
   <script src="math/Vector2.js"></script>
   <script src="math/Vector3.js"></script>
   <script src="math/Vector4.js"></script>
+  <script src="math/Euler.js"></script>
   <script src="math/Line3.js"></script>
   <script src="math/Quaternion.js"></script>
   <script src="math/Matrix3.js"></script>

+ 2 - 0
utils/build/includes/common.json

@@ -5,6 +5,8 @@
 	"src/math/Vector2.js",
 	"src/math/Vector3.js",
 	"src/math/Vector4.js",
+	"src/math/Euler.js",
+	"src/math/Rotation.js",
 	"src/math/Line3.js",
 	"src/math/Box2.js",
 	"src/math/Box3.js",

+ 2 - 0
utils/build/includes/math.json

@@ -5,6 +5,8 @@
 	"src/math/Vector2.js",
 	"src/math/Vector3.js",
 	"src/math/Vector4.js",
+	"src/math/Euler.js",
+	"src/math/Rotation.js",	
 	"src/math/Line3.js",
 	"src/math/Box2.js",
 	"src/math/Box3.js",

+ 1 - 34
utils/npm/header.js

@@ -1,34 +1 @@
-var self = self || {};
-
-// High-resulution counter: emulate window.performance.now() for THREE.CLOCK
-if( self.performance === undefined ) {
-
-	self.performance = {};
-
-}
-
-if( self.performance.now === undefined ) {
-
-	// check if we are in a Node.js environment
-	if( ( process !== undefined ) && ( process.hrtime !== undefined ) ) {
-
-		self.performance.now = function () {
-
-			var time = process.hrtime();
-			return ( time[0] + time[1] / 1e9 ) * 1000;
-
-		};
-
-	}
-	// if not Node.js revert to using the Date class
-	else {
-
-		self.performance.now = function() {
-
-			return new Date().getTime();
-
-		};
-
-	}
-
-}
+var self = self || {};