diff --git a/docs/api/math/Quaternion.html b/docs/api/math/Quaternion.html
index d4d20860e29e21..b6e0ef0166aa96 100644
--- a/docs/api/math/Quaternion.html
+++ b/docs/api/math/Quaternion.html
@@ -57,24 +57,31 @@
.copy( [page:Quaternion q] ) [page:Quaternion]
Copies values of *q* to this quaternion.
- .setFromEuler( [page:Vector3 vector] ) [page:Quaternion]
+ .setFromEuler( [page:Euler euler] ) [page:Quaternion]
- Sets this quaternion from rotation specified by Euler angles.
+ Sets this quaternion from rotation specified by Euler angle.
.setFromAxisAngle( [page:Vector3 axis], [page:Float angle] ) [page:Quaternion]
Sets this quaternion from rotation specified by axis and angle.
Adapted from [link:http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm].
- *Axis* have to be normalized, *angle* is in radians.
+ *Axis* is asumed to be normalized, *angle* is in radians.
.setFromRotationMatrix( [page:Matrix4 m] ) [page:Quaternion]
- Sets this quaternion from rotation component of *m*.
+ Sets this quaternion from rotation component of *m*.
Adapted from [link:http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm].
+ .setFromUnitVectors( [page:Vector3 vFrom], [page:Vector3 vTo] ) [page:Quaternion]
+
+ Sets this quaternion to the rotation required to rotate direction vector *vFrom* to direction vector *vTo*.
+ Adapted from [link:http://lolengine.net/blog/2013/09/18/beautiful-maths-quaternion-from-vectors].
+ *vFrom* and *vTo* are assumed to be normalized.
+
+
.inverse() [page:Quaternion]
Inverts this quaternion.
diff --git a/examples/js/controls/OrbitControls.js b/examples/js/controls/OrbitControls.js
index cc82fe3cbc891d..ae14f663304a5c 100644
--- a/examples/js/controls/OrbitControls.js
+++ b/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 );
diff --git a/examples/js/controls/TrackballControls.js b/examples/js/controls/TrackballControls.js
index e19d097fd2ba32..259f1ee1c4a472 100644
--- a/examples/js/controls/TrackballControls.js
+++ b/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 );
diff --git a/src/math/Quaternion.js b/src/math/Quaternion.js
index fb05e674618d69..2c867ecc0f07fd 100644
--- a/src/math/Quaternion.js
+++ b/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();