Skip to content

Instantly share code, notes, and snippets.

@ngokevin
Created April 27, 2018 22:24
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ngokevin/9dc36f2a75569254dbff8794847c6e23 to your computer and use it in GitHub Desktop.
Save ngokevin/9dc36f2a75569254dbff8794847c6e23 to your computer and use it in GitHub Desktop.
/**
* Pivot the scene when user enters VR to face a target.
*/
AFRAME.registerComponent('recenter', {
schema: {
target: {default: ''}
},
init: function () {
var sceneEl = this.el.sceneEl;
this.matrix = new THREE.Matrix4();
this.frustum = new THREE.Frustum();
this.rotationOffset = 0;
this.euler = new THREE.Euler();
this.euler.order = 'YXZ';
this.menuPosition = new THREE.Vector3();
this.recenter = this.recenter.bind(this);
this.checkInViewAfterRecenter = this.checkInViewAfterRecenter.bind(this);
this.target = document.querySelector(this.data.target);
// Delay to make sure we have a valid pose.
sceneEl.addEventListener('enter-vr', () => setTimeout(this.recenter, 100));
// User can also recenter the menu manually.
sceneEl.addEventListener('menudown', this.recenter);
sceneEl.addEventListener('thumbstickdown', this.recenter);
window.addEventListener('vrdisplaypresentchange', this.recenter);
},
recenter: function () {
var euler = this.euler;
euler.setFromRotationMatrix(this.el.sceneEl.camera.el.object3D.matrixWorld, 'YXZ');
this.el.object3D.rotation.y = euler.y + this.rotationOffset;
// Check if the menu is in camera frustum in next tick after a frame has rendered.
setTimeout(this.checkInViewAfterRecenter, 0);
},
/*
* Sometimes the quaternion returns the yaw in the [-180, 180] range.
* Check if the menu is in the camera frustum after recenter it to
* decide if we apply an offset or not.
*/
checkInViewAfterRecenter: (function () {
var bottomVec3 = new THREE.Vector3();
var topVec3 = new THREE.Vector3();
return function () {
var camera = this.el.sceneEl.camera;
var frustum = this.frustum;
var menuPosition = this.menuPosition;
camera.updateMatrix();
camera.updateMatrixWorld();
frustum.setFromMatrix(this.matrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse));
// Check if menu position (and its bounds) are within the frustum.
// Check bounds in case looking angled up or down, rather than menu central.
menuPosition.setFromMatrixPosition(this.target.object3D.matrixWorld);
bottomVec3.copy(menuPosition).y -= 3;
topVec3.copy(menuPosition).y += 3;
if (frustum.containsPoint(menuPosition) ||
frustum.containsPoint(bottomVec3) ||
frustum.containsPoint(topVec3)) { return; }
this.rotationOffset = this.rotationOffset === 0 ? Math.PI : 0;
// Recenter again with the new offset.
this.recenter();
};
})(),
remove: function () {
this.el.sceneEl.removeEventListener('enter-vr', this.recenter);
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment