Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: implement touch pan controls again #89

Merged
merged 1 commit into from
May 23, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions scripts/rollup-replace.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@ import replace from 'rollup-plugin-re';
const modules = [
'VRControls',
'VREffect',
'OrbitControls'
'OrbitControls',
'DeviceOrientationControls'
];

const globalReplace = function(str, pattern, replacement) {
return str.replace(new RegExp(pattern, 'g'), replacement);
};

export default function(options) {
return replace(Object.assign({
include: ['node_modules/three/examples/js/**'],
Expand All @@ -21,10 +26,20 @@ export default function(options) {
code = code.replace(`THREE.${m} =`, `import * as THREE from 'three';\nvar ${m} =`);

// change references from the global modification to the local variable
code = code.replace(new RegExp(`THREE.${m}`, 'g'), m);
code = globalReplace(code, `THREE.${m}`, m);

// export that local variable as default from this module
code += `\nexport default ${m};`;

// expose private functions so that users can manually use controls
// and we can add orientation controls
if (m === 'OrbitControls') {
code = globalReplace(code, 'function rotateLeft\\(', 'rotateLeft = function(');
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A somewhat hacky patch to OrbitControls to allow us to modify the movement a bit so that we can add on the movement from DeviceOrientationControls. I felt like this was a better alternative to Grabbing the code, storing it in our repo, and maintaining these small modifications.

code = globalReplace(code, 'function rotateUp\\(', 'rotateUp = function(');

code = globalReplace(code, 'rotateLeft', 'scope.rotateLeft');
code = globalReplace(code, 'rotateUp', 'scope.rotateUp');
}
});
return code;
}}
Expand Down
97 changes: 97 additions & 0 deletions src/orbit-orientation-controls.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import * as THREE from 'three';
import OrbitControls from 'three/examples/js/controls/OrbitControls.js';
import DeviceOrientationControls from 'three/examples/js/controls/DeviceOrientationControls.js';

/**
* Convert a quaternion to an angle
*
* Taken from https://stackoverflow.com/a/35448946
* Thanks P. Ellul
*/
function Quat2Angle(x, y, z, w) {
const test = x * y + z * w;

// singularity at north pole
if (test > 0.499) {
const yaw = 2 * Math.atan2(x, w);
const pitch = Math.PI / 2;
const roll = 0;

return new THREE.Vector3(pitch, roll, yaw);
}

// singularity at south pole
if (test < -0.499) {
const yaw = -2 * Math.atan2(x, w);
const pitch = -Math.PI / 2;
const roll = 0;

return new THREE.Vector3(pitch, roll, yaw);
}

const sqx = x * x;
const sqy = y * y;
const sqz = z * z;
const yaw = Math.atan2(2 * y * w - 2 * x * z, 1 - 2 * sqy - 2 * sqz);
const pitch = Math.asin(2 * test);
const roll = Math.atan2(2 * x * w - 2 * y * z, 1 - 2 * sqx - 2 * sqz);

return new THREE.Vector3(pitch, roll, yaw);
}

class OrbitOrientationControls {
constructor(options) {
this.object = options.camera;
this.domElement = options.canvas;
this.orbit = new OrbitControls(this.object, this.domElement);

this.speed = 0.5;
this.orbit.target.set(0, 0, -1);
this.orbit.enableZoom = false;
this.orbit.enablePan = false;
this.orbit.rotateSpeed = -this.speed;

// if orientation is supported
if (options.orientation) {
this.orientation = new DeviceOrientationControls(this.object);
}
}

update() {
// orientation updates the camera using quaternions and
// orbit updates the camera using angles. They are incompatible
// and one update overrides the other. So before
// orbit overrides orientation we convert our quaternion changes to
// an angle change. Then save the angle into orbit so that
// it will take those into account when it updates the camera and overrides
// our changes
if (this.orientation) {
this.orientation.update();

const quat = this.orientation.object.quaternion;
const currentAngle = Quat2Angle(quat.x, quat.y, quat.z, quat.w);

// we also have to store the last angle since quaternions are b
if (typeof this.lastAngle_ === 'undefined') {
this.lastAngle_ = currentAngle;
}

this.orbit.rotateLeft((this.lastAngle_.z - currentAngle.z) * (1 + this.speed));
this.orbit.rotateUp((this.lastAngle_.y - currentAngle.y) * (1 + this.speed));
this.lastAngle_ = currentAngle;
}

this.orbit.update();
}

dispose() {
this.orbit.dispose();

if (this.orientation) {
this.orientation.dispose();
}
}

}

export default OrbitOrientationControls;
27 changes: 16 additions & 11 deletions src/plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import videojs from 'video.js';
import * as THREE from 'three';
import VRControls from 'three/examples/js/controls/VRControls.js';
import VREffect from 'three/examples/js/effects/VREffect.js';
import OrbitControls from 'three/examples/js/controls/OrbitControls.js';
import OrbitOrientationContols from './orbit-orientation-controls.js';
import * as utils from './utils';

// import controls so they get regisetered with videojs
Expand Down Expand Up @@ -70,10 +70,10 @@ class VR extends Plugin {
}

this.polyfill_ = new WebVRPolyfill({
TOUCH_PANNER_DISABLED: false,
// do not show rotate instructions
ROTATE_INSTRUCTIONS_DISABLED: true
});
this.polyfill_ = new WebVRPolyfill();

this.handleVrDisplayActivate_ = videojs.bind(this, this.handleVrDisplayActivate_);
this.handleVrDisplayDeactivate_ = videojs.bind(this, this.handleVrDisplayDeactivate_);
Expand Down Expand Up @@ -476,22 +476,27 @@ class VR extends Plugin {
if (displays.length > 0) {
this.log('VR Displays found', displays);
this.vrDisplay = displays[0];
this.log('Going to use VRControls on the first one', this.vrDisplay);

// Native WebVR Head Mounted Displays (HMDs) like the HTC Vive
// also need the cardboard button to enter fully immersive mode
// so, we want to add the button if we're not polyfilled.
if (!this.vrDisplay.isPolyfilled) {
this.log('Real HMD found using VRControls', this.vrDisplay);
this.addCardboardButton_();

// We use VRControls here since we are working with an HMD
// and we only want orientation controls.
this.controls3d = new VRControls(this.camera);
}
this.controls3d = new VRControls(this.camera);
} else {
this.log('no vr displays found going to use OrbitControls');
this.controls3d = new OrbitControls(this.camera, this.renderedCanvas);
this.controls3d.target.set(0, 0, -1);
this.controls3d.enableZoom = false;
this.controls3d.enablePan = false;
this.controls3d.rotateSpeed = -0.5;
}

if (!this.controls3d) {
this.log('no HMD found Using Orbit & Orientation Controls');
this.controls3d = new OrbitOrientationContols({
camera: this.camera,
canvas: this.renderedCanvas,
orientation: videojs.browser.IS_IOS || videojs.browser.IS_ANDROID || false
});
}
this.animationFrameId_ = this.requestAnimationFrame(this.animate_);
});
Expand Down