Browse Source

Set Camera.up as the orbit axis in OrbitControls

WestLangley 11 years ago
parent
commit
796363caba

+ 11 - 4
docs/api/math/Quaternion.html

@@ -57,24 +57,31 @@
 		Copies values of *q* to this quaternion.
 		</div>
 
-		<h3>.setFromEuler( [page:Vector3 vector] ) [page:Quaternion]</h3>
+		<h3>.setFromEuler( [page:Euler euler] ) [page:Quaternion]</h3>
 		<div>
-		Sets this quaternion from rotation specified by Euler angles.
+		Sets this quaternion from rotation specified by Euler angle.
 		</div>
 
 		<h3>.setFromAxisAngle( [page:Vector3 axis], [page:Float angle] ) [page:Quaternion]</h3>
 		<div>
 		Sets this quaternion from rotation specified by axis and angle.<br />
 		Adapted from [link:http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm].<br />
-		*Axis* have to be normalized, *angle* is in radians.
+		*Axis* is asumed to be normalized, *angle* is in radians.
 		</div>
 
 		<h3>.setFromRotationMatrix( [page:Matrix4 m] ) [page:Quaternion]</h3>
 		<div>
-		Sets this quaternion from rotation component of *m*.
+		Sets this quaternion from rotation component of *m*.<br />
 		Adapted from [link:http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm].
 		</div>
 
+		<h3>.setFromUnitVectors( [page:Vector3 vFrom], [page:Vector3 vTo] ) [page:Quaternion]</h3>
+		<div>
+		Sets this quaternion to the rotation required to rotate direction vector *vFrom* to direction vector *vTo*.<br />
+		Adapted from [link:http://lolengine.net/blog/2013/09/18/beautiful-maths-quaternion-from-vectors].<br />
+		*vFrom* and *vTo* are assumed to be normalized.
+		</div>
+
 		<h3>.inverse() [page:Quaternion]</h3>
 		<div>
 		Inverts this quaternion.

+ 15 - 1
examples/js/controls/OrbitControls.js

@@ -108,6 +108,11 @@ THREE.OrbitControls = function ( object, domElement ) {
 	this.target0 = this.target.clone();
 	this.position0 = this.object.position.clone();
 
+	// so camera.up is the orbit axis
+
+	var quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) );
+	var quatInverse = quat.clone().inverse();
+
 	// events
 
 	var changeEvent = { type: 'change' };
@@ -229,6 +234,9 @@ THREE.OrbitControls = function ( object, domElement ) {
 
 		offset.copy( position ).sub( this.target );
 
+		// rotate offset to "y-axis-is-up" space
+		offset.applyQuaternion( quat );
+
 		// angle from z-axis around y-axis
 
 		var theta = Math.atan2( offset.x, offset.z );
@@ -264,6 +272,9 @@ THREE.OrbitControls = function ( object, domElement ) {
 		offset.y = radius * Math.cos( phi );
 		offset.z = radius * Math.sin( phi ) * Math.cos( theta );
 
+		// rotate offset back to "camera-up-vector-is-up" space
+		offset.applyQuaternion( quatInverse );
+
 		position.copy( this.target ).add( offset );
 
 		this.object.lookAt( this.target );
@@ -273,7 +284,7 @@ THREE.OrbitControls = function ( object, domElement ) {
 		scale = 1;
 		pan.set( 0, 0, 0 );
 
-		if ( lastPosition.distanceTo( this.object.position ) > EPS ) {
+		if ( lastPosition.distanceToSquared( this.object.position ) > EPS ) {
 
 			this.dispatchEvent( changeEvent );
 
@@ -620,6 +631,9 @@ THREE.OrbitControls = function ( object, domElement ) {
 
 	window.addEventListener( 'keydown', onKeyDown, false );
 
+	// force an update at start
+	this.update();
+
 };
 
 THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype );

+ 6 - 1
examples/js/controls/TrackballControls.js

@@ -38,6 +38,8 @@ THREE.TrackballControls = function ( object, domElement ) {
 
 	this.target = new THREE.Vector3();
 
+	var EPS = 0.000001;
+
 	var lastPosition = new THREE.Vector3();
 
 	var _state = STATE.NONE,
@@ -315,7 +317,7 @@ THREE.TrackballControls = function ( object, domElement ) {
 
 		_this.object.lookAt( _this.target );
 
-		if ( lastPosition.distanceToSquared( _this.object.position ) > 0 ) {
+		if ( lastPosition.distanceToSquared( _this.object.position ) > EPS ) {
 
 			_this.dispatchEvent( changeEvent );
 
@@ -587,6 +589,9 @@ THREE.TrackballControls = function ( object, domElement ) {
 
 	this.handleResize();
 
+	// force an update at start
+	this.update();
+
 };
 
 THREE.TrackballControls.prototype = Object.create( THREE.EventDispatcher.prototype );

+ 27 - 2
src/math/Quaternion.js

@@ -180,8 +180,9 @@ THREE.Quaternion.prototype = {
 
 	setFromAxisAngle: function ( axis, angle ) {
 
-		// from http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm
-		// axis have to be normalized
+		// http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm
+
+		// assumes axis is normalized
 
 		var halfAngle = angle / 2, s = Math.sin( halfAngle );
 
@@ -255,6 +256,30 @@ THREE.Quaternion.prototype = {
 
 	},
 
+	setFromUnitVectors: function () {
+
+		// http://lolengine.net/blog/2013/09/18/beautiful-maths-quaternion-from-vectors
+
+		// assumes direction vectors vFrom and vTo are normalized
+
+		var v1;
+
+		return function( vFrom, vTo ) {
+
+			if ( v1 === undefined ) v1 = new THREE.Vector3();
+
+			v1.crossVectors( vFrom, vTo );
+
+			this.set( v1.x, v1.y, v1.z, vFrom.dot( vTo ) + 1 ).normalize();
+
+			this._updateEuler();
+
+			return this;
+
+		}
+
+	}(),
+
 	inverse: function () {
 
 		this.conjugate().normalize();