blob: 8f9b51efb76f406917f467009c7f89ecd5774714 [file] [log] [blame]
// Copyright 2019 The Immersive Web Community Group
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
/*
Provides a simple method for tracking which XRReferenceSpace is associated with
which XRSession. Also handles the necessary logic for enabling mouse/touch-based
view rotation for inline sessions if desired.
*/
import {quat} from '../render/math/gl-matrix.js';
const LOOK_SPEED = 0.0025;
export class InlineViewerHelper {
constructor(canvas, referenceSpace) {
this.lookYaw = 0;
this.lookPitch = 0;
this.viewerHeight = 0;
this.canvas = canvas;
this.baseRefSpace = referenceSpace;
this.refSpace = referenceSpace;
this.dirty = false;
if (canvas.style) {
canvas.style.cursor = 'grab';
}
canvas.addEventListener('mousemove', (event) => {
// Only rotate when the left button is pressed
if (event.buttons & 1) {
this.rotateView(event.movementX, event.movementY);
}
});
// Keep track of touch-related state so that users can touch and drag on
// the canvas to adjust the viewer pose in an inline session.
let primaryTouch = undefined;
let prevTouchX = undefined;
let prevTouchY = undefined;
canvas.addEventListener("touchstart", (event) => {
if (primaryTouch == undefined) {
let touch = event.changedTouches[0];
primaryTouch = touch.identifier;
prevTouchX = touch.pageX;
prevTouchY = touch.pageY;
}
});
canvas.addEventListener("touchend", (event) => {
for (let touch of event.changedTouches) {
if (primaryTouch == touch.identifier) {
primaryTouch = undefined;
this.rotateView(touch.pageX - prevTouchX, touch.pageY - prevTouchY);
}
}
});
canvas.addEventListener("touchcancel", (event) => {
for (let touch of event.changedTouches) {
if (primaryTouch == touch.identifier) {
primaryTouch = undefined;
}
}
});
canvas.addEventListener("touchmove", (event) => {
for (let touch of event.changedTouches) {
if (primaryTouch == touch.identifier) {
this.rotateView(touch.pageX - prevTouchX, touch.pageY - prevTouchY);
prevTouchX = touch.pageX;
prevTouchY = touch.pageY;
}
}
});
}
setHeight(value) {
if (this.viewerHeight != value) {
this.viewerHeight = value;
}
this.dirty = true;
}
rotateView(dx, dy) {
this.lookYaw += dx * LOOK_SPEED;
this.lookPitch += dy * LOOK_SPEED;
if (this.lookPitch < -Math.PI*0.5) {
this.lookPitch = -Math.PI*0.5;
}
if (this.lookPitch > Math.PI*0.5) {
this.lookPitch = Math.PI*0.5;
}
this.dirty = true;
}
reset() {
this.lookYaw = 0;
this.lookPitch = 0;
this.refSpace = this.baseRefSpace;
this.dirty = false;
}
// XRReferenceSpace offset is immutable, so return a new reference space
// that has an updated orientation.
get referenceSpace() {
if (this.dirty) {
// Represent the rotational component of the reference space as a
// quaternion.
let invOrient = quat.create();
quat.rotateX(invOrient, invOrient, -this.lookPitch);
quat.rotateY(invOrient, invOrient, -this.lookYaw);
let xform = new XRRigidTransform(
{},
{x: invOrient[0], y: invOrient[1], z: invOrient[2], w: invOrient[3]});
this.refSpace = this.baseRefSpace.getOffsetReferenceSpace(xform);
xform = new XRRigidTransform({y: -this.viewerHeight});
this.refSpace = this.refSpace.getOffsetReferenceSpace(xform);
this.dirty = false;
}
return this.refSpace;
}
}