Files
old-allhands-vr/node_modules/webvr-polyfill/src/base.js
2017-11-19 15:16:07 +01:00

448 lines
13 KiB
JavaScript

/*
* Copyright 2015 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var Util = require('./util.js');
var WakeLock = require('./wakelock.js');
// Start at a higher number to reduce chance of conflict.
var nextDisplayId = 1000;
var hasShowDeprecationWarning = false;
var defaultLeftBounds = [0, 0, 0.5, 1];
var defaultRightBounds = [0.5, 0, 0.5, 1];
/**
* The base class for all VR frame data.
*/
function VRFrameData() {
this.leftProjectionMatrix = new Float32Array(16);
this.leftViewMatrix = new Float32Array(16);
this.rightProjectionMatrix = new Float32Array(16);
this.rightViewMatrix = new Float32Array(16);
this.pose = null;
};
/**
* The base class for all VR displays.
*/
function VRDisplay() {
this.isPolyfilled = true;
this.displayId = nextDisplayId++;
this.displayName = 'webvr-polyfill displayName';
this.depthNear = 0.01;
this.depthFar = 10000.0;
this.isConnected = true;
this.isPresenting = false;
this.capabilities = {
hasPosition: false,
hasOrientation: false,
hasExternalDisplay: false,
canPresent: false,
maxLayers: 1
};
this.stageParameters = null;
// "Private" members.
this.waitingForPresent_ = false;
this.layer_ = null;
this.fullscreenElement_ = null;
this.fullscreenWrapper_ = null;
this.fullscreenElementCachedStyle_ = null;
this.fullscreenEventTarget_ = null;
this.fullscreenChangeHandler_ = null;
this.fullscreenErrorHandler_ = null;
this.wakelock_ = new WakeLock();
}
VRDisplay.prototype.getFrameData = function(frameData) {
// TODO: Technically this should retain it's value for the duration of a frame
// but I doubt that's practical to do in javascript.
return Util.frameDataFromPose(frameData, this.getPose(), this);
};
VRDisplay.prototype.getPose = function() {
// TODO: Technically this should retain it's value for the duration of a frame
// but I doubt that's practical to do in javascript.
return this.getImmediatePose();
};
VRDisplay.prototype.requestAnimationFrame = function(callback) {
return window.requestAnimationFrame(callback);
};
VRDisplay.prototype.cancelAnimationFrame = function(id) {
return window.cancelAnimationFrame(id);
};
VRDisplay.prototype.wrapForFullscreen = function(element) {
// Don't wrap in iOS.
if (Util.isIOS()) {
return element;
}
if (!this.fullscreenWrapper_) {
this.fullscreenWrapper_ = document.createElement('div');
var cssProperties = [
'height: ' + Math.min(screen.height, screen.width) + 'px !important',
'top: 0 !important',
'left: 0 !important',
'right: 0 !important',
'border: 0',
'margin: 0',
'padding: 0',
'z-index: 999999 !important',
'position: fixed',
];
this.fullscreenWrapper_.setAttribute('style', cssProperties.join('; ') + ';');
this.fullscreenWrapper_.classList.add('webvr-polyfill-fullscreen-wrapper');
}
if (this.fullscreenElement_ == element) {
return this.fullscreenWrapper_;
}
// Remove any previously applied wrappers
this.removeFullscreenWrapper();
this.fullscreenElement_ = element;
var parent = this.fullscreenElement_.parentElement;
parent.insertBefore(this.fullscreenWrapper_, this.fullscreenElement_);
parent.removeChild(this.fullscreenElement_);
this.fullscreenWrapper_.insertBefore(this.fullscreenElement_, this.fullscreenWrapper_.firstChild);
this.fullscreenElementCachedStyle_ = this.fullscreenElement_.getAttribute('style');
var self = this;
function applyFullscreenElementStyle() {
if (!self.fullscreenElement_) {
return;
}
var cssProperties = [
'position: absolute',
'top: 0',
'left: 0',
'width: ' + Math.max(screen.width, screen.height) + 'px',
'height: ' + Math.min(screen.height, screen.width) + 'px',
'border: 0',
'margin: 0',
'padding: 0',
];
self.fullscreenElement_.setAttribute('style', cssProperties.join('; ') + ';');
}
applyFullscreenElementStyle();
return this.fullscreenWrapper_;
};
VRDisplay.prototype.removeFullscreenWrapper = function() {
if (!this.fullscreenElement_) {
return;
}
var element = this.fullscreenElement_;
if (this.fullscreenElementCachedStyle_) {
element.setAttribute('style', this.fullscreenElementCachedStyle_);
} else {
element.removeAttribute('style');
}
this.fullscreenElement_ = null;
this.fullscreenElementCachedStyle_ = null;
var parent = this.fullscreenWrapper_.parentElement;
this.fullscreenWrapper_.removeChild(element);
parent.insertBefore(element, this.fullscreenWrapper_);
parent.removeChild(this.fullscreenWrapper_);
return element;
};
VRDisplay.prototype.requestPresent = function(layers) {
var wasPresenting = this.isPresenting;
var self = this;
if (!(layers instanceof Array)) {
if (!hasShowDeprecationWarning) {
console.warn("Using a deprecated form of requestPresent. Should pass in an array of VRLayers.");
hasShowDeprecationWarning = true;
}
layers = [layers];
}
return new Promise(function(resolve, reject) {
if (!self.capabilities.canPresent) {
reject(new Error('VRDisplay is not capable of presenting.'));
return;
}
if (layers.length == 0 || layers.length > self.capabilities.maxLayers) {
reject(new Error('Invalid number of layers.'));
return;
}
var incomingLayer = layers[0];
if (!incomingLayer.source) {
/*
todo: figure out the correct behavior if the source is not provided.
see https://github.com/w3c/webvr/issues/58
*/
resolve();
return;
}
var leftBounds = incomingLayer.leftBounds || defaultLeftBounds;
var rightBounds = incomingLayer.rightBounds || defaultRightBounds;
if (wasPresenting) {
// Already presenting, just changing configuration
var layer = self.layer_;
if (layer.source !== incomingLayer.source) {
layer.source = incomingLayer.source;
}
for (var i = 0; i < 4; i++) {
if (layer.leftBounds[i] !== leftBounds[i]) {
layer.leftBounds[i] = leftBounds[i];
}
if (layer.rightBounds[i] !== rightBounds[i]) {
layer.rightBounds[i] = rightBounds[i];
}
}
resolve();
return;
}
// Was not already presenting.
self.layer_ = {
predistorted: incomingLayer.predistorted,
source: incomingLayer.source,
leftBounds: leftBounds.slice(0),
rightBounds: rightBounds.slice(0)
};
self.waitingForPresent_ = false;
if (self.layer_ && self.layer_.source) {
var fullscreenElement = self.wrapForFullscreen(self.layer_.source);
function onFullscreenChange() {
var actualFullscreenElement = Util.getFullscreenElement();
self.isPresenting = (fullscreenElement === actualFullscreenElement);
if (self.isPresenting) {
if (screen.orientation && screen.orientation.lock) {
screen.orientation.lock('landscape-primary').catch(function(error){
console.error('screen.orientation.lock() failed due to', error.message)
});
}
self.waitingForPresent_ = false;
self.beginPresent_();
resolve();
} else {
if (screen.orientation && screen.orientation.unlock) {
screen.orientation.unlock();
}
self.removeFullscreenWrapper();
self.wakelock_.release();
self.endPresent_();
self.removeFullscreenListeners_();
}
self.fireVRDisplayPresentChange_();
}
function onFullscreenError() {
if (!self.waitingForPresent_) {
return;
}
self.removeFullscreenWrapper();
self.removeFullscreenListeners_();
self.wakelock_.release();
self.waitingForPresent_ = false;
self.isPresenting = false;
reject(new Error('Unable to present.'));
}
self.addFullscreenListeners_(fullscreenElement,
onFullscreenChange, onFullscreenError);
if (Util.requestFullscreen(fullscreenElement)) {
self.wakelock_.request();
self.waitingForPresent_ = true;
} else if (Util.isIOS()) {
// *sigh* Just fake it.
self.wakelock_.request();
self.isPresenting = true;
self.beginPresent_();
self.fireVRDisplayPresentChange_();
resolve();
}
}
if (!self.waitingForPresent_ && !Util.isIOS()) {
Util.exitFullscreen();
reject(new Error('Unable to present.'));
}
});
};
VRDisplay.prototype.exitPresent = function() {
var wasPresenting = this.isPresenting;
var self = this;
this.isPresenting = false;
this.layer_ = null;
this.wakelock_.release();
return new Promise(function(resolve, reject) {
if (wasPresenting) {
if (!Util.exitFullscreen() && Util.isIOS()) {
self.endPresent_();
self.fireVRDisplayPresentChange_();
}
resolve();
} else {
reject(new Error('Was not presenting to VRDisplay.'));
}
});
};
VRDisplay.prototype.getLayers = function() {
if (this.layer_) {
return [this.layer_];
}
return [];
};
VRDisplay.prototype.fireVRDisplayPresentChange_ = function() {
var event = new CustomEvent('vrdisplaypresentchange', {detail: {display: this}});
window.dispatchEvent(event);
};
VRDisplay.prototype.addFullscreenListeners_ = function(element, changeHandler, errorHandler) {
this.removeFullscreenListeners_();
this.fullscreenEventTarget_ = element;
this.fullscreenChangeHandler_ = changeHandler;
this.fullscreenErrorHandler_ = errorHandler;
if (changeHandler) {
if (document.fullscreenEnabled) {
element.addEventListener('fullscreenchange', changeHandler, false);
} else if (document.webkitFullscreenEnabled) {
element.addEventListener('webkitfullscreenchange', changeHandler, false);
} else if (document.mozFullScreenEnabled) {
document.addEventListener('mozfullscreenchange', changeHandler, false);
} else if (document.msFullscreenEnabled) {
element.addEventListener('msfullscreenchange', changeHandler, false);
}
}
if (errorHandler) {
if (document.fullscreenEnabled) {
element.addEventListener('fullscreenerror', errorHandler, false);
} else if (document.webkitFullscreenEnabled) {
element.addEventListener('webkitfullscreenerror', errorHandler, false);
} else if (document.mozFullScreenEnabled) {
document.addEventListener('mozfullscreenerror', errorHandler, false);
} else if (document.msFullscreenEnabled) {
element.addEventListener('msfullscreenerror', errorHandler, false);
}
}
};
VRDisplay.prototype.removeFullscreenListeners_ = function() {
if (!this.fullscreenEventTarget_)
return;
var element = this.fullscreenEventTarget_;
if (this.fullscreenChangeHandler_) {
var changeHandler = this.fullscreenChangeHandler_;
element.removeEventListener('fullscreenchange', changeHandler, false);
element.removeEventListener('webkitfullscreenchange', changeHandler, false);
document.removeEventListener('mozfullscreenchange', changeHandler, false);
element.removeEventListener('msfullscreenchange', changeHandler, false);
}
if (this.fullscreenErrorHandler_) {
var errorHandler = this.fullscreenErrorHandler_;
element.removeEventListener('fullscreenerror', errorHandler, false);
element.removeEventListener('webkitfullscreenerror', errorHandler, false);
document.removeEventListener('mozfullscreenerror', errorHandler, false);
element.removeEventListener('msfullscreenerror', errorHandler, false);
}
this.fullscreenEventTarget_ = null;
this.fullscreenChangeHandler_ = null;
this.fullscreenErrorHandler_ = null;
};
VRDisplay.prototype.beginPresent_ = function() {
// Override to add custom behavior when presentation begins.
};
VRDisplay.prototype.endPresent_ = function() {
// Override to add custom behavior when presentation ends.
};
VRDisplay.prototype.submitFrame = function(pose) {
// Override to add custom behavior for frame submission.
};
VRDisplay.prototype.getEyeParameters = function(whichEye) {
// Override to return accurate eye parameters if canPresent is true.
return null;
};
/*
* Deprecated classes
*/
/**
* The base class for all VR devices. (Deprecated)
*/
function VRDevice() {
this.isPolyfilled = true;
this.hardwareUnitId = 'webvr-polyfill hardwareUnitId';
this.deviceId = 'webvr-polyfill deviceId';
this.deviceName = 'webvr-polyfill deviceName';
}
/**
* The base class for all VR HMD devices. (Deprecated)
*/
function HMDVRDevice() {
}
HMDVRDevice.prototype = new VRDevice();
/**
* The base class for all VR position sensor devices. (Deprecated)
*/
function PositionSensorVRDevice() {
}
PositionSensorVRDevice.prototype = new VRDevice();
module.exports.VRFrameData = VRFrameData;
module.exports.VRDisplay = VRDisplay;
module.exports.VRDevice = VRDevice;
module.exports.HMDVRDevice = HMDVRDevice;
module.exports.PositionSensorVRDevice = PositionSensorVRDevice;