279 lines
8.9 KiB
JavaScript
279 lines
8.9 KiB
JavaScript
/*
|
|
* Copyright 2016 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 CardboardDistorter = require('./cardboard-distorter.js');
|
|
var CardboardUI = require('./cardboard-ui.js');
|
|
var DeviceInfo = require('./device-info.js');
|
|
var Dpdb = require('./dpdb/dpdb.js');
|
|
var FusionPoseSensor = require('./sensor-fusion/fusion-pose-sensor.js');
|
|
var RotateInstructions = require('./rotate-instructions.js');
|
|
var ViewerSelector = require('./viewer-selector.js');
|
|
var VRDisplay = require('./base.js').VRDisplay;
|
|
var Util = require('./util.js');
|
|
|
|
var Eye = {
|
|
LEFT: 'left',
|
|
RIGHT: 'right'
|
|
};
|
|
|
|
/**
|
|
* VRDisplay based on mobile device parameters and DeviceMotion APIs.
|
|
*/
|
|
function CardboardVRDisplay() {
|
|
this.displayName = 'Cardboard VRDisplay (webvr-polyfill)';
|
|
|
|
this.capabilities.hasOrientation = true;
|
|
this.capabilities.canPresent = true;
|
|
|
|
// "Private" members.
|
|
this.bufferScale_ = WebVRConfig.BUFFER_SCALE;
|
|
this.poseSensor_ = new FusionPoseSensor();
|
|
this.distorter_ = null;
|
|
this.cardboardUI_ = null;
|
|
|
|
this.dpdb_ = new Dpdb(true, this.onDeviceParamsUpdated_.bind(this));
|
|
this.deviceInfo_ = new DeviceInfo(this.dpdb_.getDeviceParams());
|
|
|
|
this.viewerSelector_ = new ViewerSelector();
|
|
this.viewerSelector_.on('change', this.onViewerChanged_.bind(this));
|
|
|
|
// Set the correct initial viewer.
|
|
this.deviceInfo_.setViewer(this.viewerSelector_.getCurrentViewer());
|
|
|
|
if (!WebVRConfig.ROTATE_INSTRUCTIONS_DISABLED) {
|
|
this.rotateInstructions_ = new RotateInstructions();
|
|
}
|
|
|
|
if (Util.isIOS()) {
|
|
// Listen for resize events to workaround this awful Safari bug.
|
|
window.addEventListener('resize', this.onResize_.bind(this));
|
|
}
|
|
}
|
|
CardboardVRDisplay.prototype = new VRDisplay();
|
|
|
|
CardboardVRDisplay.prototype.getImmediatePose = function() {
|
|
return {
|
|
position: this.poseSensor_.getPosition(),
|
|
orientation: this.poseSensor_.getOrientation(),
|
|
linearVelocity: null,
|
|
linearAcceleration: null,
|
|
angularVelocity: null,
|
|
angularAcceleration: null
|
|
};
|
|
};
|
|
|
|
CardboardVRDisplay.prototype.resetPose = function() {
|
|
this.poseSensor_.resetPose();
|
|
};
|
|
|
|
CardboardVRDisplay.prototype.getEyeParameters = function(whichEye) {
|
|
var offset = [this.deviceInfo_.viewer.interLensDistance * 0.5, 0.0, 0.0];
|
|
var fieldOfView;
|
|
|
|
// TODO: FoV can be a little expensive to compute. Cache when device params change.
|
|
if (whichEye == Eye.LEFT) {
|
|
offset[0] *= -1.0;
|
|
fieldOfView = this.deviceInfo_.getFieldOfViewLeftEye();
|
|
} else if (whichEye == Eye.RIGHT) {
|
|
fieldOfView = this.deviceInfo_.getFieldOfViewRightEye();
|
|
} else {
|
|
console.error('Invalid eye provided: %s', whichEye);
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
fieldOfView: fieldOfView,
|
|
offset: offset,
|
|
// TODO: Should be able to provide better values than these.
|
|
renderWidth: this.deviceInfo_.device.width * 0.5 * this.bufferScale_,
|
|
renderHeight: this.deviceInfo_.device.height * this.bufferScale_,
|
|
};
|
|
};
|
|
|
|
CardboardVRDisplay.prototype.onDeviceParamsUpdated_ = function(newParams) {
|
|
if (Util.isDebug()) {
|
|
console.log('DPDB reported that device params were updated.');
|
|
}
|
|
this.deviceInfo_.updateDeviceParams(newParams);
|
|
|
|
if (this.distorter_) {
|
|
this.distorter_.updateDeviceInfo(this.deviceInfo_);
|
|
}
|
|
};
|
|
|
|
CardboardVRDisplay.prototype.updateBounds_ = function () {
|
|
if (this.layer_ && this.distorter_ && (this.layer_.leftBounds || this.layer_.rightBounds)) {
|
|
this.distorter_.setTextureBounds(this.layer_.leftBounds, this.layer_.rightBounds);
|
|
}
|
|
};
|
|
|
|
CardboardVRDisplay.prototype.beginPresent_ = function() {
|
|
var gl = this.layer_.source.getContext('webgl');
|
|
if (!gl)
|
|
gl = this.layer_.source.getContext('experimental-webgl');
|
|
if (!gl)
|
|
gl = this.layer_.source.getContext('webgl2');
|
|
|
|
if (!gl)
|
|
return; // Can't do distortion without a WebGL context.
|
|
|
|
// Provides a way to opt out of distortion
|
|
if (this.layer_.predistorted) {
|
|
if (!WebVRConfig.CARDBOARD_UI_DISABLED) {
|
|
gl.canvas.width = Util.getScreenWidth() * this.bufferScale_;
|
|
gl.canvas.height = Util.getScreenHeight() * this.bufferScale_;
|
|
this.cardboardUI_ = new CardboardUI(gl);
|
|
}
|
|
} else {
|
|
// Create a new distorter for the target context
|
|
this.distorter_ = new CardboardDistorter(gl);
|
|
this.distorter_.updateDeviceInfo(this.deviceInfo_);
|
|
this.cardboardUI_ = this.distorter_.cardboardUI;
|
|
}
|
|
|
|
if (this.cardboardUI_) {
|
|
this.cardboardUI_.listen(function(e) {
|
|
// Options clicked.
|
|
this.viewerSelector_.show(this.layer_.source.parentElement);
|
|
e.stopPropagation();
|
|
e.preventDefault();
|
|
}.bind(this), function(e) {
|
|
// Back clicked.
|
|
this.exitPresent();
|
|
e.stopPropagation();
|
|
e.preventDefault();
|
|
}.bind(this));
|
|
}
|
|
|
|
if (this.rotateInstructions_) {
|
|
if (Util.isLandscapeMode() && Util.isMobile()) {
|
|
// In landscape mode, temporarily show the "put into Cardboard"
|
|
// interstitial. Otherwise, do the default thing.
|
|
this.rotateInstructions_.showTemporarily(3000, this.layer_.source.parentElement);
|
|
} else {
|
|
this.rotateInstructions_.update();
|
|
}
|
|
}
|
|
|
|
// Listen for orientation change events in order to show interstitial.
|
|
this.orientationHandler = this.onOrientationChange_.bind(this);
|
|
window.addEventListener('orientationchange', this.orientationHandler);
|
|
|
|
// Listen for present display change events in order to update distorter dimensions
|
|
this.vrdisplaypresentchangeHandler = this.updateBounds_.bind(this);
|
|
window.addEventListener('vrdisplaypresentchange', this.vrdisplaypresentchangeHandler);
|
|
|
|
// Fire this event initially, to give geometry-distortion clients the chance
|
|
// to do something custom.
|
|
this.fireVRDisplayDeviceParamsChange_();
|
|
};
|
|
|
|
CardboardVRDisplay.prototype.endPresent_ = function() {
|
|
if (this.distorter_) {
|
|
this.distorter_.destroy();
|
|
this.distorter_ = null;
|
|
}
|
|
if (this.cardboardUI_) {
|
|
this.cardboardUI_.destroy();
|
|
this.cardboardUI_ = null;
|
|
}
|
|
|
|
if (this.rotateInstructions_) {
|
|
this.rotateInstructions_.hide();
|
|
}
|
|
this.viewerSelector_.hide();
|
|
|
|
window.removeEventListener('orientationchange', this.orientationHandler);
|
|
window.removeEventListener('vrdisplaypresentchange', this.vrdisplaypresentchangeHandler);
|
|
};
|
|
|
|
CardboardVRDisplay.prototype.submitFrame = function(pose) {
|
|
if (this.distorter_) {
|
|
this.distorter_.submitFrame();
|
|
} else if (this.cardboardUI_ && this.layer_) {
|
|
// Hack for predistorted: true.
|
|
var canvas = this.layer_.source.getContext('webgl').canvas;
|
|
if (canvas.width != this.lastWidth || canvas.height != this.lastHeight) {
|
|
this.cardboardUI_.onResize();
|
|
}
|
|
this.lastWidth = canvas.width;
|
|
this.lastHeight = canvas.height;
|
|
|
|
// Render the Cardboard UI.
|
|
this.cardboardUI_.render();
|
|
}
|
|
};
|
|
|
|
CardboardVRDisplay.prototype.onOrientationChange_ = function(e) {
|
|
// Hide the viewer selector.
|
|
this.viewerSelector_.hide();
|
|
|
|
// Update the rotate instructions.
|
|
if (this.rotateInstructions_) {
|
|
this.rotateInstructions_.update();
|
|
}
|
|
|
|
this.onResize_();
|
|
};
|
|
|
|
CardboardVRDisplay.prototype.onResize_ = function(e) {
|
|
if (this.layer_) {
|
|
var gl = this.layer_.source.getContext('webgl');
|
|
// Size the CSS canvas.
|
|
// Added padding on right and bottom because iPhone 5 will not
|
|
// hide the URL bar unless content is bigger than the screen.
|
|
// This will not be visible as long as the container element (e.g. body)
|
|
// is set to 'overflow: hidden'.
|
|
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 10px 10px 0',
|
|
];
|
|
gl.canvas.setAttribute('style', cssProperties.join('; ') + ';');
|
|
|
|
Util.safariCssSizeWorkaround(gl.canvas);
|
|
}
|
|
};
|
|
|
|
CardboardVRDisplay.prototype.onViewerChanged_ = function(viewer) {
|
|
this.deviceInfo_.setViewer(viewer);
|
|
|
|
if (this.distorter_) {
|
|
// Update the distortion appropriately.
|
|
this.distorter_.updateDeviceInfo(this.deviceInfo_);
|
|
}
|
|
|
|
// Fire a new event containing viewer and device parameters for clients that
|
|
// want to implement their own geometry-based distortion.
|
|
this.fireVRDisplayDeviceParamsChange_();
|
|
};
|
|
|
|
CardboardVRDisplay.prototype.fireVRDisplayDeviceParamsChange_ = function() {
|
|
var event = new CustomEvent('vrdisplaydeviceparamschange', {
|
|
detail: {
|
|
vrdisplay: this,
|
|
deviceInfo: this.deviceInfo_,
|
|
}
|
|
});
|
|
window.dispatchEvent(event);
|
|
};
|
|
|
|
module.exports = CardboardVRDisplay;
|