Merge pull request #187 from supermedium/recenter

recenter
This commit is contained in:
Kevin Ngo
2018-11-17 02:00:35 -08:00
committed by GitHub
6 changed files with 329 additions and 470 deletions

View File

@@ -1,76 +1,48 @@
/**
* Pivot the scene when user enters VR to face the links.
* Recenter camera back to facing down negative Z axis at 0, 0, 0.
*/
AFRAME.registerComponent('recenter', {
schema: {
enabled: {default: true},
target: {default: ''}
enabled: {default: true}
},
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);
document.querySelectorAll('[tracked-controls]').forEach(controlEl => {
controlEl.addEventListener('menudown', this.recenter);
controlEl.addEventListener('thumbstickdown', this.recenter);
});
},
recenter: function () {
var euler = this.euler;
if (!this.data.enabled) { return; }
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();
recenter: (function () {
var euler = new THREE.Euler();
var matrix = new THREE.Matrix4();
var rotationMatrix = new THREE.Matrix4();
var translationMatrix = new THREE.Matrix4();
return function () {
var camera = this.el.sceneEl.camera;
var frustum = this.frustum;
var menuPosition = this.menuPosition;
const el = this.el;
camera.updateMatrix();
camera.updateMatrixWorld();
frustum.setFromMatrix(this.matrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse));
if (!this.data.enabled) { return; }
// 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;
const camera = el.sceneEl.camera.el.object3D;
if (frustum.containsPoint(menuPosition) ||
frustum.containsPoint(bottomVec3) ||
frustum.containsPoint(topVec3)) { return; }
// Reset matrix.
matrix.identity();
this.rotationOffset = this.rotationOffset === 0 ? Math.PI : 0;
// Recenter again with the new offset.
this.recenter();
// Create matrix to reset Y rotation.
euler.set(0, -1 * camera.rotation.y, 0);
rotationMatrix.makeRotationFromEuler(euler);
// Create matrix to zero position.
translationMatrix.makeTranslation(-1 * camera.position.x, 0, -1 * camera.position.z);
// Multiply and decompose back to object3D.
matrix.multiply(rotationMatrix).multiply(translationMatrix);
matrix.decompose(el.object3D.position, el.object3D.quaternion, el.object3D.scale);
el.object3D.updateMatrixWorld(true);
};
})(),
remove: function () {
this.el.sceneEl.removeEventListener('enter-vr', this.recenter);
}
})()
});

View File

@@ -33,7 +33,7 @@
effect-bloom="strength: 1"
gpu-preloader
loading-screen="backgroundColor: #000;"
overlay="objects: #rightHand, #leftHand, [mixin~='cursorMesh'], .overlay"
overlay="objects: .overlay"
pool__beat-arrow-blue="mixin: arrowBlueBeat; size: 10; container: #beatContainer"
pool__beat-arrow-red="mixin: arrowRedBeat; size: 10; container: #beatContainer"
pool__beat-dot-blue="mixin: dotBlueBeat; size: 10; container: #beatContainer"
@@ -92,13 +92,18 @@
id="sparkParticles"
particleplayer="src: #sparksJSON; color: #fcc; pscale: 0.7; on: explode; scale: 0.3; loop: false; img: #spark2Img; dur: 700; count: 50%; animateScale: true; initialScale: 3 0.5 1; finalScale: 0.1 0.5 1"></a-entity>
<!-- Player. -->
<a-mixin
id="cursorMesh"
material="shader: flat; transparent: true; src: #cursorMeshImg; depthTest: false"
sub-object="from: #cursorLaser; name: glow"></a-mixin>
<a-entity id="cameraRig">
<!--
Player.
Apply overlay to camera rig, not controllers, because overlay will reparent to
an empty scene, disabling ability to move and rotate the camera rig having effect
on controller matrix world.
-->
<a-entity id="cameraRig" class="overlay" bind__recenter="enabled: !isPlaying">
<a-entity id="camera" position="0 1.6 0.5" camera look-controls wasd-controls>
<a-entity
id="cameraCollider"
@@ -180,6 +185,7 @@
<a-entity
id="{{ hand }}CursorMesh"
class="overlay"
mixin="cursorMesh"
bind__cursor-mesh="active: {{ hand }}RaycasterActive && !isPLaying"
cursor-mesh="cursorEl: #{{ hand }}Hand"
@@ -205,8 +211,12 @@
mixin="raycaster"
cursor="rayOrigin: mouse"
raycaster="objects: [raycastable]"></a-entity>
<a-entity id="mouseCursorMesh" mixin="cursorMesh" cursor-mesh="cursorEl: #mouseCursor"
bind__cursor-mesh="active: menuActive"></a-entity>
<a-entity
id="mouseCursorMesh"
class="overlay"
mixin="cursorMesh"
cursor-mesh="cursorEl: #mouseCursor"
bind__cursor-mesh="active: menuActive"></a-entity>
{% endif %}
</a-scene>