webvr js meetup initial commit

This commit is contained in:
Senad Uka
2017-03-11 15:22:17 +01:00
commit 3f640b55db
761 changed files with 264174 additions and 0 deletions

4
node_modules/webvr-polyfill/.jscsrc generated vendored Normal file
View File

@@ -0,0 +1,4 @@
{
"preset": "google",
"maximumLineLength": 120
}

16
node_modules/webvr-polyfill/.npmignore generated vendored Normal file
View File

@@ -0,0 +1,16 @@
*.log
*.pid
*.seed
*.un~
*~
.DS_Store
.netrwhist
.node_repl_history
.npm
Session.vim
[._]*.s[a-w][a-z]
[._]s[a-w][a-z]
examples
logs
node_modules
pids

21
node_modules/webvr-polyfill/CONTRIBUTING generated vendored Normal file
View File

@@ -0,0 +1,21 @@
# Building
This project uses browserify to manage dependencies and build. Watchify is
especially convenient to preserve the write-and-reload model of development.
This package lives in the npm index.
Relevant commands:
npm install - installs the dependencies.
npm build - builds the module.
npm watch - auto-builds the module whenever any source changes.
# Updating the npm entry
Once changes are made, a new version can be published to the index using the
following commands:
npm version <NEW_VERSION>
npm publish
git push --tags

202
node_modules/webvr-polyfill/COPYING generated vendored Normal file
View File

@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2015 Google Inc
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.

116
node_modules/webvr-polyfill/README.md generated vendored Normal file
View File

@@ -0,0 +1,116 @@
# WebVR Polyfill
A JavaScript implementation of the [WebVR spec][spec]. This project lets you use
WebVR today, without requiring a [special][moz] [browser][cr] build. It also
lets you view the same content without requiring a virtual reality viewer.
Take a look at [basic WebVR samples][samples] that use this polyfill.
[moz]: http://mozvr.com/
[cr]: https://drive.google.com/folderview?id=0BzudLt22BqGRbW9WTHMtOWMzNjQ
[samples]: https://toji.github.io/webvr-samples/
[spec]: https://mozvr.github.io/webvr-spec/
## Implementation
The polyfill decides which VRDisplays to provide, depending on the configuration
of your browser. Mobile devices provide the `CardboardVRDisplay`. Desktop devices
use the `MouseKeyboardVRDisplay`.
`CardboardVRDisplay` uses DeviceMotionEvents to implement a complementary
filter which does [sensor fusion and pose prediction][fusion] to provide
orientation tracking. It can also render in stereo mode, and includes mesh-based
lens distortion. This display also includes user interface elements in VR mode
to make the VR experience more intuitive, including:
- A gear icon to select your VR viewer.
- A back button to exit VR mode.
- An interstitial which only appears in portrait orientation, requesting you switch
into landscape orientation (if [orientation lock][ol] is not available).
`MouseKeyboardVRDisplay` uses mouse events to allow you to do the equivalent of
mouselook. It also uses keyboard arrows keys to look around the scene
with the keyboard.
[fusion]: http://smus.com/sensor-fusion-prediction-webvr/
[ol]: https://www.w3.org/TR/screen-orientation/
## Configuration
The polyfill can be configured and debugged with various options. The following
are supported:
```javascript
WebVRConfig = {
// Flag to disabled the UI in VR Mode.
CARDBOARD_UI_DISABLED: false, // Default: false
// Forces availability of VR mode, even for non-mobile devices.
FORCE_ENABLE_VR: true, // Default: false.
// Complementary filter coefficient. 0 for accelerometer, 1 for gyro.
K_FILTER: 0.98, // Default: 0.98.
// Flag to disable the instructions to rotate your device.
ROTATE_INSTRUCTIONS_DISABLED: false, // Default: false.
// How far into the future to predict during fast motion (in seconds).
PREDICTION_TIME_S: 0.040, // Default: 0.040.
// Flag to disable touch panner. In case you have your own touch controls.
TOUCH_PANNER_DISABLED: false, // Default: true.
// Enable yaw panning only, disabling roll and pitch. This can be useful
// for panoramas with nothing interesting above or below.
YAW_ONLY: true, // Default: false.
// To disable keyboard and mouse controls, if you want to use your own
// implementation.
MOUSE_KEYBOARD_CONTROLS_DISABLED: true, // Default: false.
// Prevent the polyfill from initializing immediately. Requires the app
// to call InitializeWebVRPolyfill() before it can be used.
DEFER_INITIALIZATION: true, // Default: false.
// Enable the deprecated version of the API (navigator.getVRDevices).
ENABLE_DEPRECATED_API: true, // Default: false.
// Scales the recommended buffer size reported by WebVR, which can improve
// performance.
BUFFER_SCALE: 0.5, // Default: 0.5.
// Allow VRDisplay.submitFrame to change gl bindings, which is more
// efficient if the application code will re-bind its resources on the
// next frame anyway. This has been seen to cause rendering glitches with
// THREE.js.
// Dirty bindings include: gl.FRAMEBUFFER_BINDING, gl.CURRENT_PROGRAM,
// gl.ARRAY_BUFFER_BINDING, gl.ELEMENT_ARRAY_BUFFER_BINDING,
// and gl.TEXTURE_BINDING_2D for texture unit 0.
DIRTY_SUBMIT_FRAME_BINDINGS: true // Default: false.
}
```
## Performance
Performance is critical for VR. If you find your application is too sluggish,
consider tweaking some of the above parameters. In particular, keeping
`BUFFER_SCALE` at 0.5 (the default) will likely help a lot.
## Development
If you'd like to contribute to the `webvr-poyfill` library, check out
the repository and install
[Node](https://nodejs.org/en/download/package-manager/) and the dependencies:
```bash
git clone https://github.com/borismus/webvr-polyfill
cd webvr-polyfill
npm install
```
## License
This program is free software for both commercial and non-commercial use,
distributed under the [Apache 2.0 License](COPYING).

6312
node_modules/webvr-polyfill/build/webvr-polyfill.js generated vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

8
node_modules/webvr-polyfill/index.html generated vendored Normal file
View File

@@ -0,0 +1,8 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="refresh" content="0; url=examples/">
</head>
<body></body>
</html>

98
node_modules/webvr-polyfill/package.json generated vendored Normal file
View File

@@ -0,0 +1,98 @@
{
"_args": [
[
{
"raw": "webvr-polyfill@latest",
"scope": null,
"escapedName": "webvr-polyfill",
"name": "webvr-polyfill",
"rawSpec": "latest",
"spec": "latest",
"type": "tag"
},
"/Users/smus/Projects/webvr-boilerplate"
]
],
"_from": "webvr-polyfill@latest",
"_id": "webvr-polyfill@0.9.25",
"_inCache": true,
"_installable": true,
"_location": "/webvr-polyfill",
"_nodeVersion": "6.7.0",
"_npmOperationalInternal": {
"host": "packages-18-east.internal.npmjs.com",
"tmp": "tmp/webvr-polyfill-0.9.25.tgz_1481250847048_0.2061113598756492"
},
"_npmUser": {
"name": "smus",
"email": "boris@smus.com"
},
"_npmVersion": "3.10.7",
"_phantomChildren": {},
"_requested": {
"raw": "webvr-polyfill@latest",
"scope": null,
"escapedName": "webvr-polyfill",
"name": "webvr-polyfill",
"rawSpec": "latest",
"spec": "latest",
"type": "tag"
},
"_requiredBy": [
"/"
],
"_resolved": "https://registry.npmjs.org/webvr-polyfill/-/webvr-polyfill-0.9.25.tgz",
"_shasum": "0ac1c4bff5433893ac7335144f8aca164cb397fa",
"_shrinkwrap": null,
"_spec": "webvr-polyfill@latest",
"_where": "/Users/smus/Projects/webvr-boilerplate",
"authors": [
"Boris Smus <boris@smus.com>",
"Brandon Jones <tojiro@gmail.com>"
],
"bugs": {
"url": "https://github.com/googlevr/webvr-polyfill/issues"
},
"dependencies": {
"eventemitter3": "^2.0.2",
"object-assign": "^4.0.1"
},
"description": "Use WebVR today, on mobile or desktop, without requiring a special browser build.",
"devDependencies": {
"browserify": "latest",
"derequire": "latest",
"watchify": "latest"
},
"directories": {},
"dist": {
"shasum": "0ac1c4bff5433893ac7335144f8aca164cb397fa",
"tarball": "https://registry.npmjs.org/webvr-polyfill/-/webvr-polyfill-0.9.25.tgz"
},
"gitHead": "1047fd29bc33d62d922a13c9c29c51fb23eb7cd5",
"homepage": "https://github.com/googlevr/webvr-polyfill",
"keywords": [
"vr",
"webvr"
],
"license": "Apache-2.0",
"main": "build/webvr-polyfill.js",
"maintainers": [
{
"name": "smus",
"email": "boris@smus.com"
}
],
"name": "webvr-polyfill",
"optionalDependencies": {},
"readme": "ERROR: No README data found!",
"repository": {
"type": "git",
"url": "git+https://github.com/googlevr/webvr-polyfill.git"
},
"scripts": {
"build": "browserify src/main.js --standalone WebVRPolyfill | derequire > build/webvr-polyfill.js",
"min": "browserify src/main.js --standalone WebVRPolyfill | derequire | uglifyjs -c > build/webvr-polyfill.min.js",
"watch": "watchify src/main.js --standalone WebVRPolyfill -v -d -o 'derequire > build/webvr-polyfill.js'"
},
"version": "0.9.25"
}

447
node_modules/webvr-polyfill/src/base.js generated vendored Normal file
View File

@@ -0,0 +1,447 @@
/*
* 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;

648
node_modules/webvr-polyfill/src/cardboard-distorter.js generated vendored Normal file
View File

@@ -0,0 +1,648 @@
/*
* 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 CardboardUI = require('./cardboard-ui.js');
var Util = require('./util.js');
var WGLUPreserveGLState = require('./deps/wglu-preserve-state.js');
var distortionVS = [
'attribute vec2 position;',
'attribute vec3 texCoord;',
'varying vec2 vTexCoord;',
'uniform vec4 viewportOffsetScale[2];',
'void main() {',
' vec4 viewport = viewportOffsetScale[int(texCoord.z)];',
' vTexCoord = (texCoord.xy * viewport.zw) + viewport.xy;',
' gl_Position = vec4( position, 1.0, 1.0 );',
'}',
].join('\n');
var distortionFS = [
'precision mediump float;',
'uniform sampler2D diffuse;',
'varying vec2 vTexCoord;',
'void main() {',
' gl_FragColor = texture2D(diffuse, vTexCoord);',
'}',
].join('\n');
/**
* A mesh-based distorter.
*/
function CardboardDistorter(gl) {
this.gl = gl;
this.ctxAttribs = gl.getContextAttributes();
this.meshWidth = 20;
this.meshHeight = 20;
this.bufferScale = WebVRConfig.BUFFER_SCALE;
this.bufferWidth = gl.drawingBufferWidth;
this.bufferHeight = gl.drawingBufferHeight;
// Patching support
this.realBindFramebuffer = gl.bindFramebuffer;
this.realEnable = gl.enable;
this.realDisable = gl.disable;
this.realColorMask = gl.colorMask;
this.realClearColor = gl.clearColor;
this.realViewport = gl.viewport;
if (!Util.isIOS()) {
this.realCanvasWidth = Object.getOwnPropertyDescriptor(gl.canvas.__proto__, 'width');
this.realCanvasHeight = Object.getOwnPropertyDescriptor(gl.canvas.__proto__, 'height');
}
this.isPatched = false;
// State tracking
this.lastBoundFramebuffer = null;
this.cullFace = false;
this.depthTest = false;
this.blend = false;
this.scissorTest = false;
this.stencilTest = false;
this.viewport = [0, 0, 0, 0];
this.colorMask = [true, true, true, true];
this.clearColor = [0, 0, 0, 0];
this.attribs = {
position: 0,
texCoord: 1
};
this.program = Util.linkProgram(gl, distortionVS, distortionFS, this.attribs);
this.uniforms = Util.getProgramUniforms(gl, this.program);
this.viewportOffsetScale = new Float32Array(8);
this.setTextureBounds();
this.vertexBuffer = gl.createBuffer();
this.indexBuffer = gl.createBuffer();
this.indexCount = 0;
this.renderTarget = gl.createTexture();
this.framebuffer = gl.createFramebuffer();
this.depthStencilBuffer = null;
this.depthBuffer = null;
this.stencilBuffer = null;
if (this.ctxAttribs.depth && this.ctxAttribs.stencil) {
this.depthStencilBuffer = gl.createRenderbuffer();
} else if (this.ctxAttribs.depth) {
this.depthBuffer = gl.createRenderbuffer();
} else if (this.ctxAttribs.stencil) {
this.stencilBuffer = gl.createRenderbuffer();
}
this.patch();
this.onResize();
if (!WebVRConfig.CARDBOARD_UI_DISABLED) {
this.cardboardUI = new CardboardUI(gl);
}
};
/**
* Tears down all the resources created by the distorter and removes any
* patches.
*/
CardboardDistorter.prototype.destroy = function() {
var gl = this.gl;
this.unpatch();
gl.deleteProgram(this.program);
gl.deleteBuffer(this.vertexBuffer);
gl.deleteBuffer(this.indexBuffer);
gl.deleteTexture(this.renderTarget);
gl.deleteFramebuffer(this.framebuffer);
if (this.depthStencilBuffer) {
gl.deleteRenderbuffer(this.depthStencilBuffer);
}
if (this.depthBuffer) {
gl.deleteRenderbuffer(this.depthBuffer);
}
if (this.stencilBuffer) {
gl.deleteRenderbuffer(this.stencilBuffer);
}
if (this.cardboardUI) {
this.cardboardUI.destroy();
}
};
/**
* Resizes the backbuffer to match the canvas width and height.
*/
CardboardDistorter.prototype.onResize = function() {
var gl = this.gl;
var self = this;
var glState = [
gl.RENDERBUFFER_BINDING,
gl.TEXTURE_BINDING_2D, gl.TEXTURE0
];
WGLUPreserveGLState(gl, glState, function(gl) {
// Bind real backbuffer and clear it once. We don't need to clear it again
// after that because we're overwriting the same area every frame.
self.realBindFramebuffer.call(gl, gl.FRAMEBUFFER, null);
// Put things in a good state
if (self.scissorTest) { self.realDisable.call(gl, gl.SCISSOR_TEST); }
self.realColorMask.call(gl, true, true, true, true);
self.realViewport.call(gl, 0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
self.realClearColor.call(gl, 0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
// Now bind and resize the fake backbuffer
self.realBindFramebuffer.call(gl, gl.FRAMEBUFFER, self.framebuffer);
gl.bindTexture(gl.TEXTURE_2D, self.renderTarget);
gl.texImage2D(gl.TEXTURE_2D, 0, self.ctxAttribs.alpha ? gl.RGBA : gl.RGB,
self.bufferWidth, self.bufferHeight, 0,
self.ctxAttribs.alpha ? gl.RGBA : gl.RGB, gl.UNSIGNED_BYTE, null);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, self.renderTarget, 0);
if (self.ctxAttribs.depth && self.ctxAttribs.stencil) {
gl.bindRenderbuffer(gl.RENDERBUFFER, self.depthStencilBuffer);
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL,
self.bufferWidth, self.bufferHeight);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT,
gl.RENDERBUFFER, self.depthStencilBuffer);
} else if (self.ctxAttribs.depth) {
gl.bindRenderbuffer(gl.RENDERBUFFER, self.depthBuffer);
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16,
self.bufferWidth, self.bufferHeight);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT,
gl.RENDERBUFFER, self.depthBuffer);
} else if (self.ctxAttribs.stencil) {
gl.bindRenderbuffer(gl.RENDERBUFFER, self.stencilBuffer);
gl.renderbufferStorage(gl.RENDERBUFFER, gl.STENCIL_INDEX8,
self.bufferWidth, self.bufferHeight);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT,
gl.RENDERBUFFER, self.stencilBuffer);
}
if (!gl.checkFramebufferStatus(gl.FRAMEBUFFER) === gl.FRAMEBUFFER_COMPLETE) {
console.error('Framebuffer incomplete!');
}
self.realBindFramebuffer.call(gl, gl.FRAMEBUFFER, self.lastBoundFramebuffer);
if (self.scissorTest) { self.realEnable.call(gl, gl.SCISSOR_TEST); }
self.realColorMask.apply(gl, self.colorMask);
self.realViewport.apply(gl, self.viewport);
self.realClearColor.apply(gl, self.clearColor);
});
if (this.cardboardUI) {
this.cardboardUI.onResize();
}
};
CardboardDistorter.prototype.patch = function() {
if (this.isPatched) {
return;
}
var self = this;
var canvas = this.gl.canvas;
var gl = this.gl;
if (!Util.isIOS()) {
canvas.width = Util.getScreenWidth() * this.bufferScale;
canvas.height = Util.getScreenHeight() * this.bufferScale;
Object.defineProperty(canvas, 'width', {
configurable: true,
enumerable: true,
get: function() {
return self.bufferWidth;
},
set: function(value) {
self.bufferWidth = value;
self.realCanvasWidth.set.call(canvas, value);
self.onResize();
}
});
Object.defineProperty(canvas, 'height', {
configurable: true,
enumerable: true,
get: function() {
return self.bufferHeight;
},
set: function(value) {
self.bufferHeight = value;
self.realCanvasHeight.set.call(canvas, value);
self.onResize();
}
});
}
this.lastBoundFramebuffer = gl.getParameter(gl.FRAMEBUFFER_BINDING);
if (this.lastBoundFramebuffer == null) {
this.lastBoundFramebuffer = this.framebuffer;
this.gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
}
this.gl.bindFramebuffer = function(target, framebuffer) {
self.lastBoundFramebuffer = framebuffer ? framebuffer : self.framebuffer;
// Silently make calls to bind the default framebuffer bind ours instead.
self.realBindFramebuffer.call(gl, target, self.lastBoundFramebuffer);
};
this.cullFace = gl.getParameter(gl.CULL_FACE);
this.depthTest = gl.getParameter(gl.DEPTH_TEST);
this.blend = gl.getParameter(gl.BLEND);
this.scissorTest = gl.getParameter(gl.SCISSOR_TEST);
this.stencilTest = gl.getParameter(gl.STENCIL_TEST);
gl.enable = function(pname) {
switch (pname) {
case gl.CULL_FACE: self.cullFace = true; break;
case gl.DEPTH_TEST: self.depthTest = true; break;
case gl.BLEND: self.blend = true; break;
case gl.SCISSOR_TEST: self.scissorTest = true; break;
case gl.STENCIL_TEST: self.stencilTest = true; break;
}
self.realEnable.call(gl, pname);
};
gl.disable = function(pname) {
switch (pname) {
case gl.CULL_FACE: self.cullFace = false; break;
case gl.DEPTH_TEST: self.depthTest = false; break;
case gl.BLEND: self.blend = false; break;
case gl.SCISSOR_TEST: self.scissorTest = false; break;
case gl.STENCIL_TEST: self.stencilTest = false; break;
}
self.realDisable.call(gl, pname);
};
this.colorMask = gl.getParameter(gl.COLOR_WRITEMASK);
gl.colorMask = function(r, g, b, a) {
self.colorMask[0] = r;
self.colorMask[1] = g;
self.colorMask[2] = b;
self.colorMask[3] = a;
self.realColorMask.call(gl, r, g, b, a);
};
this.clearColor = gl.getParameter(gl.COLOR_CLEAR_VALUE);
gl.clearColor = function(r, g, b, a) {
self.clearColor[0] = r;
self.clearColor[1] = g;
self.clearColor[2] = b;
self.clearColor[3] = a;
self.realClearColor.call(gl, r, g, b, a);
};
this.viewport = gl.getParameter(gl.VIEWPORT);
gl.viewport = function(x, y, w, h) {
self.viewport[0] = x;
self.viewport[1] = y;
self.viewport[2] = w;
self.viewport[3] = h;
self.realViewport.call(gl, x, y, w, h);
};
this.isPatched = true;
Util.safariCssSizeWorkaround(canvas);
};
CardboardDistorter.prototype.unpatch = function() {
if (!this.isPatched) {
return;
}
var gl = this.gl;
var canvas = this.gl.canvas;
if (!Util.isIOS()) {
Object.defineProperty(canvas, 'width', this.realCanvasWidth);
Object.defineProperty(canvas, 'height', this.realCanvasHeight);
}
canvas.width = this.bufferWidth;
canvas.height = this.bufferHeight;
gl.bindFramebuffer = this.realBindFramebuffer;
gl.enable = this.realEnable;
gl.disable = this.realDisable;
gl.colorMask = this.realColorMask;
gl.clearColor = this.realClearColor;
gl.viewport = this.realViewport;
// Check to see if our fake backbuffer is bound and bind the real backbuffer
// if that's the case.
if (this.lastBoundFramebuffer == this.framebuffer) {
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
}
this.isPatched = false;
setTimeout(function() {
Util.safariCssSizeWorkaround(canvas);
}, 1);
};
CardboardDistorter.prototype.setTextureBounds = function(leftBounds, rightBounds) {
if (!leftBounds) {
leftBounds = [0, 0, 0.5, 1];
}
if (!rightBounds) {
rightBounds = [0.5, 0, 0.5, 1];
}
// Left eye
this.viewportOffsetScale[0] = leftBounds[0]; // X
this.viewportOffsetScale[1] = leftBounds[1]; // Y
this.viewportOffsetScale[2] = leftBounds[2]; // Width
this.viewportOffsetScale[3] = leftBounds[3]; // Height
// Right eye
this.viewportOffsetScale[4] = rightBounds[0]; // X
this.viewportOffsetScale[5] = rightBounds[1]; // Y
this.viewportOffsetScale[6] = rightBounds[2]; // Width
this.viewportOffsetScale[7] = rightBounds[3]; // Height
};
/**
* Performs distortion pass on the injected backbuffer, rendering it to the real
* backbuffer.
*/
CardboardDistorter.prototype.submitFrame = function() {
var gl = this.gl;
var self = this;
var glState = [];
if (!WebVRConfig.DIRTY_SUBMIT_FRAME_BINDINGS) {
glState.push(
gl.CURRENT_PROGRAM,
gl.ARRAY_BUFFER_BINDING,
gl.ELEMENT_ARRAY_BUFFER_BINDING,
gl.TEXTURE_BINDING_2D, gl.TEXTURE0
);
}
WGLUPreserveGLState(gl, glState, function(gl) {
// Bind the real default framebuffer
self.realBindFramebuffer.call(gl, gl.FRAMEBUFFER, null);
// Make sure the GL state is in a good place
if (self.cullFace) { self.realDisable.call(gl, gl.CULL_FACE); }
if (self.depthTest) { self.realDisable.call(gl, gl.DEPTH_TEST); }
if (self.blend) { self.realDisable.call(gl, gl.BLEND); }
if (self.scissorTest) { self.realDisable.call(gl, gl.SCISSOR_TEST); }
if (self.stencilTest) { self.realDisable.call(gl, gl.STENCIL_TEST); }
self.realColorMask.call(gl, true, true, true, true);
self.realViewport.call(gl, 0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
// If the backbuffer has an alpha channel clear every frame so the page
// doesn't show through.
if (self.ctxAttribs.alpha || Util.isIOS()) {
self.realClearColor.call(gl, 0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
}
// Bind distortion program and mesh
gl.useProgram(self.program);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, self.indexBuffer);
gl.bindBuffer(gl.ARRAY_BUFFER, self.vertexBuffer);
gl.enableVertexAttribArray(self.attribs.position);
gl.enableVertexAttribArray(self.attribs.texCoord);
gl.vertexAttribPointer(self.attribs.position, 2, gl.FLOAT, false, 20, 0);
gl.vertexAttribPointer(self.attribs.texCoord, 3, gl.FLOAT, false, 20, 8);
gl.activeTexture(gl.TEXTURE0);
gl.uniform1i(self.uniforms.diffuse, 0);
gl.bindTexture(gl.TEXTURE_2D, self.renderTarget);
gl.uniform4fv(self.uniforms.viewportOffsetScale, self.viewportOffsetScale);
// Draws both eyes
gl.drawElements(gl.TRIANGLES, self.indexCount, gl.UNSIGNED_SHORT, 0);
if (self.cardboardUI) {
self.cardboardUI.renderNoState();
}
// Bind the fake default framebuffer again
self.realBindFramebuffer.call(self.gl, gl.FRAMEBUFFER, self.framebuffer);
// If preserveDrawingBuffer == false clear the framebuffer
if (!self.ctxAttribs.preserveDrawingBuffer) {
self.realClearColor.call(gl, 0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
}
if (!WebVRConfig.DIRTY_SUBMIT_FRAME_BINDINGS) {
self.realBindFramebuffer.call(gl, gl.FRAMEBUFFER, self.lastBoundFramebuffer);
}
// Restore state
if (self.cullFace) { self.realEnable.call(gl, gl.CULL_FACE); }
if (self.depthTest) { self.realEnable.call(gl, gl.DEPTH_TEST); }
if (self.blend) { self.realEnable.call(gl, gl.BLEND); }
if (self.scissorTest) { self.realEnable.call(gl, gl.SCISSOR_TEST); }
if (self.stencilTest) { self.realEnable.call(gl, gl.STENCIL_TEST); }
self.realColorMask.apply(gl, self.colorMask);
self.realViewport.apply(gl, self.viewport);
if (self.ctxAttribs.alpha || !self.ctxAttribs.preserveDrawingBuffer) {
self.realClearColor.apply(gl, self.clearColor);
}
});
// Workaround for the fact that Safari doesn't allow us to patch the canvas
// width and height correctly. After each submit frame check to see what the
// real backbuffer size has been set to and resize the fake backbuffer size
// to match.
if (Util.isIOS()) {
var canvas = gl.canvas;
if (canvas.width != self.bufferWidth || canvas.height != self.bufferHeight) {
self.bufferWidth = canvas.width;
self.bufferHeight = canvas.height;
self.onResize();
}
}
};
/**
* Call when the deviceInfo has changed. At this point we need
* to re-calculate the distortion mesh.
*/
CardboardDistorter.prototype.updateDeviceInfo = function(deviceInfo) {
var gl = this.gl;
var self = this;
var glState = [gl.ARRAY_BUFFER_BINDING, gl.ELEMENT_ARRAY_BUFFER_BINDING];
WGLUPreserveGLState(gl, glState, function(gl) {
var vertices = self.computeMeshVertices_(self.meshWidth, self.meshHeight, deviceInfo);
gl.bindBuffer(gl.ARRAY_BUFFER, self.vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
// Indices don't change based on device parameters, so only compute once.
if (!self.indexCount) {
var indices = self.computeMeshIndices_(self.meshWidth, self.meshHeight);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, self.indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
self.indexCount = indices.length;
}
});
};
/**
* Build the distortion mesh vertices.
* Based on code from the Unity cardboard plugin.
*/
CardboardDistorter.prototype.computeMeshVertices_ = function(width, height, deviceInfo) {
var vertices = new Float32Array(2 * width * height * 5);
var lensFrustum = deviceInfo.getLeftEyeVisibleTanAngles();
var noLensFrustum = deviceInfo.getLeftEyeNoLensTanAngles();
var viewport = deviceInfo.getLeftEyeVisibleScreenRect(noLensFrustum);
var vidx = 0;
var iidx = 0;
for (var e = 0; e < 2; e++) {
for (var j = 0; j < height; j++) {
for (var i = 0; i < width; i++, vidx++) {
var u = i / (width - 1);
var v = j / (height - 1);
// Grid points regularly spaced in StreoScreen, and barrel distorted in
// the mesh.
var s = u;
var t = v;
var x = Util.lerp(lensFrustum[0], lensFrustum[2], u);
var y = Util.lerp(lensFrustum[3], lensFrustum[1], v);
var d = Math.sqrt(x * x + y * y);
var r = deviceInfo.distortion.distortInverse(d);
var p = x * r / d;
var q = y * r / d;
u = (p - noLensFrustum[0]) / (noLensFrustum[2] - noLensFrustum[0]);
v = (q - noLensFrustum[3]) / (noLensFrustum[1] - noLensFrustum[3]);
// Convert u,v to mesh screen coordinates.
var aspect = deviceInfo.device.widthMeters / deviceInfo.device.heightMeters;
// FIXME: The original Unity plugin multiplied U by the aspect ratio
// and didn't multiply either value by 2, but that seems to get it
// really close to correct looking for me. I hate this kind of "Don't
// know why it works" code though, and wold love a more logical
// explanation of what needs to happen here.
u = (viewport.x + u * viewport.width - 0.5) * 2.0; //* aspect;
v = (viewport.y + v * viewport.height - 0.5) * 2.0;
vertices[(vidx * 5) + 0] = u; // position.x
vertices[(vidx * 5) + 1] = v; // position.y
vertices[(vidx * 5) + 2] = s; // texCoord.x
vertices[(vidx * 5) + 3] = t; // texCoord.y
vertices[(vidx * 5) + 4] = e; // texCoord.z (viewport index)
}
}
var w = lensFrustum[2] - lensFrustum[0];
lensFrustum[0] = -(w + lensFrustum[0]);
lensFrustum[2] = w - lensFrustum[2];
w = noLensFrustum[2] - noLensFrustum[0];
noLensFrustum[0] = -(w + noLensFrustum[0]);
noLensFrustum[2] = w - noLensFrustum[2];
viewport.x = 1 - (viewport.x + viewport.width);
}
return vertices;
}
/**
* Build the distortion mesh indices.
* Based on code from the Unity cardboard plugin.
*/
CardboardDistorter.prototype.computeMeshIndices_ = function(width, height) {
var indices = new Uint16Array(2 * (width - 1) * (height - 1) * 6);
var halfwidth = width / 2;
var halfheight = height / 2;
var vidx = 0;
var iidx = 0;
for (var e = 0; e < 2; e++) {
for (var j = 0; j < height; j++) {
for (var i = 0; i < width; i++, vidx++) {
if (i == 0 || j == 0)
continue;
// Build a quad. Lower right and upper left quadrants have quads with
// the triangle diagonal flipped to get the vignette to interpolate
// correctly.
if ((i <= halfwidth) == (j <= halfheight)) {
// Quad diagonal lower left to upper right.
indices[iidx++] = vidx;
indices[iidx++] = vidx - width - 1;
indices[iidx++] = vidx - width;
indices[iidx++] = vidx - width - 1;
indices[iidx++] = vidx;
indices[iidx++] = vidx - 1;
} else {
// Quad diagonal upper left to lower right.
indices[iidx++] = vidx - 1;
indices[iidx++] = vidx - width;
indices[iidx++] = vidx;
indices[iidx++] = vidx - width;
indices[iidx++] = vidx - 1;
indices[iidx++] = vidx - width - 1;
}
}
}
}
return indices;
};
CardboardDistorter.prototype.getOwnPropertyDescriptor_ = function(proto, attrName) {
var descriptor = Object.getOwnPropertyDescriptor(proto, attrName);
// In some cases (ahem... Safari), the descriptor returns undefined get and
// set fields. In this case, we need to create a synthetic property
// descriptor. This works around some of the issues in
// https://github.com/borismus/webvr-polyfill/issues/46
if (descriptor.get === undefined || descriptor.set === undefined) {
descriptor.configurable = true;
descriptor.enumerable = true;
descriptor.get = function() {
return this.getAttribute(attrName);
};
descriptor.set = function(val) {
this.setAttribute(attrName, val);
};
}
return descriptor;
};
module.exports = CardboardDistorter;

286
node_modules/webvr-polyfill/src/cardboard-ui.js generated vendored Normal file
View File

@@ -0,0 +1,286 @@
/*
* 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 Util = require('./util.js');
var WGLUPreserveGLState = require('./deps/wglu-preserve-state.js');
var uiVS = [
'attribute vec2 position;',
'uniform mat4 projectionMat;',
'void main() {',
' gl_Position = projectionMat * vec4( position, -1.0, 1.0 );',
'}',
].join('\n');
var uiFS = [
'precision mediump float;',
'uniform vec4 color;',
'void main() {',
' gl_FragColor = color;',
'}',
].join('\n');
var DEG2RAD = Math.PI/180.0;
// The gear has 6 identical sections, each spanning 60 degrees.
var kAnglePerGearSection = 60;
// Half-angle of the span of the outer rim.
var kOuterRimEndAngle = 12;
// Angle between the middle of the outer rim and the start of the inner rim.
var kInnerRimBeginAngle = 20;
// Distance from center to outer rim, normalized so that the entire model
// fits in a [-1, 1] x [-1, 1] square.
var kOuterRadius = 1;
// Distance from center to depressed rim, in model units.
var kMiddleRadius = 0.75;
// Radius of the inner hollow circle, in model units.
var kInnerRadius = 0.3125;
// Center line thickness in DP.
var kCenterLineThicknessDp = 4;
// Button width in DP.
var kButtonWidthDp = 28;
// Factor to scale the touch area that responds to the touch.
var kTouchSlopFactor = 1.5;
var Angles = [
0, kOuterRimEndAngle, kInnerRimBeginAngle,
kAnglePerGearSection - kInnerRimBeginAngle,
kAnglePerGearSection - kOuterRimEndAngle
];
/**
* Renders the alignment line and "options" gear. It is assumed that the canvas
* this is rendered into covers the entire screen (or close to it.)
*/
function CardboardUI(gl) {
this.gl = gl;
this.attribs = {
position: 0
};
this.program = Util.linkProgram(gl, uiVS, uiFS, this.attribs);
this.uniforms = Util.getProgramUniforms(gl, this.program);
this.vertexBuffer = gl.createBuffer();
this.gearOffset = 0;
this.gearVertexCount = 0;
this.arrowOffset = 0;
this.arrowVertexCount = 0;
this.projMat = new Float32Array(16);
this.listener = null;
this.onResize();
};
/**
* Tears down all the resources created by the UI renderer.
*/
CardboardUI.prototype.destroy = function() {
var gl = this.gl;
if (this.listener) {
gl.canvas.removeEventListener('click', this.listener, false);
}
gl.deleteProgram(this.program);
gl.deleteBuffer(this.vertexBuffer);
};
/**
* Adds a listener to clicks on the gear and back icons
*/
CardboardUI.prototype.listen = function(optionsCallback, backCallback) {
var canvas = this.gl.canvas;
this.listener = function(event) {
var midline = canvas.clientWidth / 2;
var buttonSize = kButtonWidthDp * kTouchSlopFactor;
// Check to see if the user clicked on (or around) the gear icon
if (event.clientX > midline - buttonSize &&
event.clientX < midline + buttonSize &&
event.clientY > canvas.clientHeight - buttonSize) {
optionsCallback(event);
}
// Check to see if the user clicked on (or around) the back icon
else if (event.clientX < buttonSize && event.clientY < buttonSize) {
backCallback(event);
}
};
canvas.addEventListener('click', this.listener, false);
};
/**
* Builds the UI mesh.
*/
CardboardUI.prototype.onResize = function() {
var gl = this.gl;
var self = this;
var glState = [
gl.ARRAY_BUFFER_BINDING
];
WGLUPreserveGLState(gl, glState, function(gl) {
var vertices = [];
var midline = gl.drawingBufferWidth / 2;
// Assumes your canvas width and height is scaled proportionately.
// TODO(smus): The following causes buttons to become huge on iOS, but seems
// like the right thing to do. For now, added a hack. But really, investigate why.
var dps = (gl.drawingBufferWidth / (screen.width * window.devicePixelRatio));
if (!Util.isIOS()) {
dps *= window.devicePixelRatio;
}
var lineWidth = kCenterLineThicknessDp * dps / 2;
var buttonSize = kButtonWidthDp * kTouchSlopFactor * dps;
var buttonScale = kButtonWidthDp * dps / 2;
var buttonBorder = ((kButtonWidthDp * kTouchSlopFactor) - kButtonWidthDp) * dps;
// Build centerline
vertices.push(midline - lineWidth, buttonSize);
vertices.push(midline - lineWidth, gl.drawingBufferHeight);
vertices.push(midline + lineWidth, buttonSize);
vertices.push(midline + lineWidth, gl.drawingBufferHeight);
// Build gear
self.gearOffset = (vertices.length / 2);
function addGearSegment(theta, r) {
var angle = (90 - theta) * DEG2RAD;
var x = Math.cos(angle);
var y = Math.sin(angle);
vertices.push(kInnerRadius * x * buttonScale + midline, kInnerRadius * y * buttonScale + buttonScale);
vertices.push(r * x * buttonScale + midline, r * y * buttonScale + buttonScale);
}
for (var i = 0; i <= 6; i++) {
var segmentTheta = i * kAnglePerGearSection;
addGearSegment(segmentTheta, kOuterRadius);
addGearSegment(segmentTheta + kOuterRimEndAngle, kOuterRadius);
addGearSegment(segmentTheta + kInnerRimBeginAngle, kMiddleRadius);
addGearSegment(segmentTheta + (kAnglePerGearSection - kInnerRimBeginAngle), kMiddleRadius);
addGearSegment(segmentTheta + (kAnglePerGearSection - kOuterRimEndAngle), kOuterRadius);
}
self.gearVertexCount = (vertices.length / 2) - self.gearOffset;
// Build back arrow
self.arrowOffset = (vertices.length / 2);
function addArrowVertex(x, y) {
vertices.push(buttonBorder + x, gl.drawingBufferHeight - buttonBorder - y);
}
var angledLineWidth = lineWidth / Math.sin(45 * DEG2RAD);
addArrowVertex(0, buttonScale);
addArrowVertex(buttonScale, 0);
addArrowVertex(buttonScale + angledLineWidth, angledLineWidth);
addArrowVertex(angledLineWidth, buttonScale + angledLineWidth);
addArrowVertex(angledLineWidth, buttonScale - angledLineWidth);
addArrowVertex(0, buttonScale);
addArrowVertex(buttonScale, buttonScale * 2);
addArrowVertex(buttonScale + angledLineWidth, (buttonScale * 2) - angledLineWidth);
addArrowVertex(angledLineWidth, buttonScale - angledLineWidth);
addArrowVertex(0, buttonScale);
addArrowVertex(angledLineWidth, buttonScale - lineWidth);
addArrowVertex(kButtonWidthDp * dps, buttonScale - lineWidth);
addArrowVertex(angledLineWidth, buttonScale + lineWidth);
addArrowVertex(kButtonWidthDp * dps, buttonScale + lineWidth);
self.arrowVertexCount = (vertices.length / 2) - self.arrowOffset;
// Buffer data
gl.bindBuffer(gl.ARRAY_BUFFER, self.vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
});
};
/**
* Performs distortion pass on the injected backbuffer, rendering it to the real
* backbuffer.
*/
CardboardUI.prototype.render = function() {
var gl = this.gl;
var self = this;
var glState = [
gl.CULL_FACE,
gl.DEPTH_TEST,
gl.BLEND,
gl.SCISSOR_TEST,
gl.STENCIL_TEST,
gl.COLOR_WRITEMASK,
gl.VIEWPORT,
gl.CURRENT_PROGRAM,
gl.ARRAY_BUFFER_BINDING
];
WGLUPreserveGLState(gl, glState, function(gl) {
// Make sure the GL state is in a good place
gl.disable(gl.CULL_FACE);
gl.disable(gl.DEPTH_TEST);
gl.disable(gl.BLEND);
gl.disable(gl.SCISSOR_TEST);
gl.disable(gl.STENCIL_TEST);
gl.colorMask(true, true, true, true);
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
self.renderNoState();
});
};
CardboardUI.prototype.renderNoState = function() {
var gl = this.gl;
// Bind distortion program and mesh
gl.useProgram(this.program);
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
gl.enableVertexAttribArray(this.attribs.position);
gl.vertexAttribPointer(this.attribs.position, 2, gl.FLOAT, false, 8, 0);
gl.uniform4f(this.uniforms.color, 1.0, 1.0, 1.0, 1.0);
Util.orthoMatrix(this.projMat, 0, gl.drawingBufferWidth, 0, gl.drawingBufferHeight, 0.1, 1024.0);
gl.uniformMatrix4fv(this.uniforms.projectionMat, false, this.projMat);
// Draws UI element
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
gl.drawArrays(gl.TRIANGLE_STRIP, this.gearOffset, this.gearVertexCount);
gl.drawArrays(gl.TRIANGLE_STRIP, this.arrowOffset, this.arrowVertexCount);
};
module.exports = CardboardUI;

278
node_modules/webvr-polyfill/src/cardboard-vr-display.js generated vendored Normal file
View File

@@ -0,0 +1,278 @@
/*
* 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;

View File

@@ -0,0 +1,164 @@
/*
Copyright (c) 2016, Brandon Jones.
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.
*/
/*
Caches specified GL state, runs a callback, and restores the cached state when
done.
Example usage:
var savedState = [
gl.ARRAY_BUFFER_BINDING,
// TEXTURE_BINDING_2D or _CUBE_MAP must always be followed by the texure unit.
gl.TEXTURE_BINDING_2D, gl.TEXTURE0,
gl.CLEAR_COLOR,
];
// After this call the array buffer, texture unit 0, active texture, and clear
// color will be restored. The viewport will remain changed, however, because
// gl.VIEWPORT was not included in the savedState list.
WGLUPreserveGLState(gl, savedState, function(gl) {
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, ....);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, ...);
gl.clearColor(1, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
});
Note that this is not intended to be fast. Managing state in your own code to
avoid redundant state setting and querying will always be faster. This function
is most useful for cases where you may not have full control over the WebGL
calls being made, such as tooling or effect injectors.
*/
function WGLUPreserveGLState(gl, bindings, callback) {
if (!bindings) {
callback(gl);
return;
}
var boundValues = [];
var activeTexture = null;
for (var i = 0; i < bindings.length; ++i) {
var binding = bindings[i];
switch (binding) {
case gl.TEXTURE_BINDING_2D:
case gl.TEXTURE_BINDING_CUBE_MAP:
var textureUnit = bindings[++i];
if (textureUnit < gl.TEXTURE0 || textureUnit > gl.TEXTURE31) {
console.error("TEXTURE_BINDING_2D or TEXTURE_BINDING_CUBE_MAP must be followed by a valid texture unit");
boundValues.push(null, null);
break;
}
if (!activeTexture) {
activeTexture = gl.getParameter(gl.ACTIVE_TEXTURE);
}
gl.activeTexture(textureUnit);
boundValues.push(gl.getParameter(binding), null);
break;
case gl.ACTIVE_TEXTURE:
activeTexture = gl.getParameter(gl.ACTIVE_TEXTURE);
boundValues.push(null);
break;
default:
boundValues.push(gl.getParameter(binding));
break;
}
}
callback(gl);
for (var i = 0; i < bindings.length; ++i) {
var binding = bindings[i];
var boundValue = boundValues[i];
switch (binding) {
case gl.ACTIVE_TEXTURE:
break; // Ignore this binding, since we special-case it to happen last.
case gl.ARRAY_BUFFER_BINDING:
gl.bindBuffer(gl.ARRAY_BUFFER, boundValue);
break;
case gl.COLOR_CLEAR_VALUE:
gl.clearColor(boundValue[0], boundValue[1], boundValue[2], boundValue[3]);
break;
case gl.COLOR_WRITEMASK:
gl.colorMask(boundValue[0], boundValue[1], boundValue[2], boundValue[3]);
break;
case gl.CURRENT_PROGRAM:
gl.useProgram(boundValue);
break;
case gl.ELEMENT_ARRAY_BUFFER_BINDING:
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, boundValue);
break;
case gl.FRAMEBUFFER_BINDING:
gl.bindFramebuffer(gl.FRAMEBUFFER, boundValue);
break;
case gl.RENDERBUFFER_BINDING:
gl.bindRenderbuffer(gl.RENDERBUFFER, boundValue);
break;
case gl.TEXTURE_BINDING_2D:
var textureUnit = bindings[++i];
if (textureUnit < gl.TEXTURE0 || textureUnit > gl.TEXTURE31)
break;
gl.activeTexture(textureUnit);
gl.bindTexture(gl.TEXTURE_2D, boundValue);
break;
case gl.TEXTURE_BINDING_CUBE_MAP:
var textureUnit = bindings[++i];
if (textureUnit < gl.TEXTURE0 || textureUnit > gl.TEXTURE31)
break;
gl.activeTexture(textureUnit);
gl.bindTexture(gl.TEXTURE_CUBE_MAP, boundValue);
break;
case gl.VIEWPORT:
gl.viewport(boundValue[0], boundValue[1], boundValue[2], boundValue[3]);
break;
case gl.BLEND:
case gl.CULL_FACE:
case gl.DEPTH_TEST:
case gl.SCISSOR_TEST:
case gl.STENCIL_TEST:
if (boundValue) {
gl.enable(binding);
} else {
gl.disable(binding);
}
break;
default:
console.log("No GL restore behavior for 0x" + binding.toString(16));
break;
}
if (activeTexture) {
gl.activeTexture(activeTexture);
}
}
}
module.exports = WGLUPreserveGLState;

365
node_modules/webvr-polyfill/src/device-info.js generated vendored Normal file
View File

@@ -0,0 +1,365 @@
/*
* 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 Distortion = require('./distortion/distortion.js');
var MathUtil = require('./math-util.js');
var Util = require('./util.js');
function Device(params) {
this.width = params.width || Util.getScreenWidth();
this.height = params.height || Util.getScreenHeight();
this.widthMeters = params.widthMeters;
this.heightMeters = params.heightMeters;
this.bevelMeters = params.bevelMeters;
}
// Fallback Android device (based on Nexus 5 measurements) for use when
// we can't recognize an Android device.
var DEFAULT_ANDROID = new Device({
widthMeters: 0.110,
heightMeters: 0.062,
bevelMeters: 0.004
});
// Fallback iOS device (based on iPhone6) for use when
// we can't recognize an Android device.
var DEFAULT_IOS = new Device({
widthMeters: 0.1038,
heightMeters: 0.0584,
bevelMeters: 0.004
});
var Viewers = {
CardboardV1: new CardboardViewer({
id: 'CardboardV1',
label: 'Cardboard I/O 2014',
fov: 40,
interLensDistance: 0.060,
baselineLensDistance: 0.035,
screenLensDistance: 0.042,
distortionCoefficients: [0.441, 0.156],
inverseCoefficients: [-0.4410035, 0.42756155, -0.4804439, 0.5460139,
-0.58821183, 0.5733938, -0.48303202, 0.33299083, -0.17573841,
0.0651772, -0.01488963, 0.001559834]
}),
CardboardV2: new CardboardViewer({
id: 'CardboardV2',
label: 'Cardboard I/O 2015',
fov: 60,
interLensDistance: 0.064,
baselineLensDistance: 0.035,
screenLensDistance: 0.039,
distortionCoefficients: [0.34, 0.55],
inverseCoefficients: [-0.33836704, -0.18162185, 0.862655, -1.2462051,
1.0560602, -0.58208317, 0.21609078, -0.05444823, 0.009177956,
-9.904169E-4, 6.183535E-5, -1.6981803E-6]
})
};
var DEFAULT_LEFT_CENTER = {x: 0.5, y: 0.5};
var DEFAULT_RIGHT_CENTER = {x: 0.5, y: 0.5};
/**
* Manages information about the device and the viewer.
*
* deviceParams indicates the parameters of the device to use (generally
* obtained from dpdb.getDeviceParams()). Can be null to mean no device
* params were found.
*/
function DeviceInfo(deviceParams) {
this.viewer = Viewers.CardboardV2;
this.updateDeviceParams(deviceParams);
this.distortion = new Distortion(this.viewer.distortionCoefficients);
}
DeviceInfo.prototype.updateDeviceParams = function(deviceParams) {
this.device = this.determineDevice_(deviceParams) || this.device;
};
DeviceInfo.prototype.getDevice = function() {
return this.device;
};
DeviceInfo.prototype.setViewer = function(viewer) {
this.viewer = viewer;
this.distortion = new Distortion(this.viewer.distortionCoefficients);
};
DeviceInfo.prototype.determineDevice_ = function(deviceParams) {
if (!deviceParams) {
// No parameters, so use a default.
if (Util.isIOS()) {
console.warn('Using fallback iOS device measurements.');
return DEFAULT_IOS;
} else {
console.warn('Using fallback Android device measurements.');
return DEFAULT_ANDROID;
}
}
// Compute device screen dimensions based on deviceParams.
var METERS_PER_INCH = 0.0254;
var metersPerPixelX = METERS_PER_INCH / deviceParams.xdpi;
var metersPerPixelY = METERS_PER_INCH / deviceParams.ydpi;
var width = Util.getScreenWidth();
var height = Util.getScreenHeight();
return new Device({
widthMeters: metersPerPixelX * width,
heightMeters: metersPerPixelY * height,
bevelMeters: deviceParams.bevelMm * 0.001,
});
};
/**
* Calculates field of view for the left eye.
*/
DeviceInfo.prototype.getDistortedFieldOfViewLeftEye = function() {
var viewer = this.viewer;
var device = this.device;
var distortion = this.distortion;
// Device.height and device.width for device in portrait mode, so transpose.
var eyeToScreenDistance = viewer.screenLensDistance;
var outerDist = (device.widthMeters - viewer.interLensDistance) / 2;
var innerDist = viewer.interLensDistance / 2;
var bottomDist = viewer.baselineLensDistance - device.bevelMeters;
var topDist = device.heightMeters - bottomDist;
var outerAngle = MathUtil.radToDeg * Math.atan(
distortion.distort(outerDist / eyeToScreenDistance));
var innerAngle = MathUtil.radToDeg * Math.atan(
distortion.distort(innerDist / eyeToScreenDistance));
var bottomAngle = MathUtil.radToDeg * Math.atan(
distortion.distort(bottomDist / eyeToScreenDistance));
var topAngle = MathUtil.radToDeg * Math.atan(
distortion.distort(topDist / eyeToScreenDistance));
return {
leftDegrees: Math.min(outerAngle, viewer.fov),
rightDegrees: Math.min(innerAngle, viewer.fov),
downDegrees: Math.min(bottomAngle, viewer.fov),
upDegrees: Math.min(topAngle, viewer.fov)
};
};
/**
* Calculates the tan-angles from the maximum FOV for the left eye for the
* current device and screen parameters.
*/
DeviceInfo.prototype.getLeftEyeVisibleTanAngles = function() {
var viewer = this.viewer;
var device = this.device;
var distortion = this.distortion;
// Tan-angles from the max FOV.
var fovLeft = Math.tan(-MathUtil.degToRad * viewer.fov);
var fovTop = Math.tan(MathUtil.degToRad * viewer.fov);
var fovRight = Math.tan(MathUtil.degToRad * viewer.fov);
var fovBottom = Math.tan(-MathUtil.degToRad * viewer.fov);
// Viewport size.
var halfWidth = device.widthMeters / 4;
var halfHeight = device.heightMeters / 2;
// Viewport center, measured from left lens position.
var verticalLensOffset = (viewer.baselineLensDistance - device.bevelMeters - halfHeight);
var centerX = viewer.interLensDistance / 2 - halfWidth;
var centerY = -verticalLensOffset;
var centerZ = viewer.screenLensDistance;
// Tan-angles of the viewport edges, as seen through the lens.
var screenLeft = distortion.distort((centerX - halfWidth) / centerZ);
var screenTop = distortion.distort((centerY + halfHeight) / centerZ);
var screenRight = distortion.distort((centerX + halfWidth) / centerZ);
var screenBottom = distortion.distort((centerY - halfHeight) / centerZ);
// Compare the two sets of tan-angles and take the value closer to zero on each side.
var result = new Float32Array(4);
result[0] = Math.max(fovLeft, screenLeft);
result[1] = Math.min(fovTop, screenTop);
result[2] = Math.min(fovRight, screenRight);
result[3] = Math.max(fovBottom, screenBottom);
return result;
};
/**
* Calculates the tan-angles from the maximum FOV for the left eye for the
* current device and screen parameters, assuming no lenses.
*/
DeviceInfo.prototype.getLeftEyeNoLensTanAngles = function() {
var viewer = this.viewer;
var device = this.device;
var distortion = this.distortion;
var result = new Float32Array(4);
// Tan-angles from the max FOV.
var fovLeft = distortion.distortInverse(Math.tan(-MathUtil.degToRad * viewer.fov));
var fovTop = distortion.distortInverse(Math.tan(MathUtil.degToRad * viewer.fov));
var fovRight = distortion.distortInverse(Math.tan(MathUtil.degToRad * viewer.fov));
var fovBottom = distortion.distortInverse(Math.tan(-MathUtil.degToRad * viewer.fov));
// Viewport size.
var halfWidth = device.widthMeters / 4;
var halfHeight = device.heightMeters / 2;
// Viewport center, measured from left lens position.
var verticalLensOffset = (viewer.baselineLensDistance - device.bevelMeters - halfHeight);
var centerX = viewer.interLensDistance / 2 - halfWidth;
var centerY = -verticalLensOffset;
var centerZ = viewer.screenLensDistance;
// Tan-angles of the viewport edges, as seen through the lens.
var screenLeft = (centerX - halfWidth) / centerZ;
var screenTop = (centerY + halfHeight) / centerZ;
var screenRight = (centerX + halfWidth) / centerZ;
var screenBottom = (centerY - halfHeight) / centerZ;
// Compare the two sets of tan-angles and take the value closer to zero on each side.
result[0] = Math.max(fovLeft, screenLeft);
result[1] = Math.min(fovTop, screenTop);
result[2] = Math.min(fovRight, screenRight);
result[3] = Math.max(fovBottom, screenBottom);
return result;
};
/**
* Calculates the screen rectangle visible from the left eye for the
* current device and screen parameters.
*/
DeviceInfo.prototype.getLeftEyeVisibleScreenRect = function(undistortedFrustum) {
var viewer = this.viewer;
var device = this.device;
var dist = viewer.screenLensDistance;
var eyeX = (device.widthMeters - viewer.interLensDistance) / 2;
var eyeY = viewer.baselineLensDistance - device.bevelMeters;
var left = (undistortedFrustum[0] * dist + eyeX) / device.widthMeters;
var top = (undistortedFrustum[1] * dist + eyeY) / device.heightMeters;
var right = (undistortedFrustum[2] * dist + eyeX) / device.widthMeters;
var bottom = (undistortedFrustum[3] * dist + eyeY) / device.heightMeters;
return {
x: left,
y: bottom,
width: right - left,
height: top - bottom
};
};
DeviceInfo.prototype.getFieldOfViewLeftEye = function(opt_isUndistorted) {
return opt_isUndistorted ? this.getUndistortedFieldOfViewLeftEye() :
this.getDistortedFieldOfViewLeftEye();
};
DeviceInfo.prototype.getFieldOfViewRightEye = function(opt_isUndistorted) {
var fov = this.getFieldOfViewLeftEye(opt_isUndistorted);
return {
leftDegrees: fov.rightDegrees,
rightDegrees: fov.leftDegrees,
upDegrees: fov.upDegrees,
downDegrees: fov.downDegrees
};
};
/**
* Calculates undistorted field of view for the left eye.
*/
DeviceInfo.prototype.getUndistortedFieldOfViewLeftEye = function() {
var p = this.getUndistortedParams_();
return {
leftDegrees: MathUtil.radToDeg * Math.atan(p.outerDist),
rightDegrees: MathUtil.radToDeg * Math.atan(p.innerDist),
downDegrees: MathUtil.radToDeg * Math.atan(p.bottomDist),
upDegrees: MathUtil.radToDeg * Math.atan(p.topDist)
};
};
DeviceInfo.prototype.getUndistortedViewportLeftEye = function() {
var p = this.getUndistortedParams_();
var viewer = this.viewer;
var device = this.device;
// Distances stored in local variables are in tan-angle units unless otherwise
// noted.
var eyeToScreenDistance = viewer.screenLensDistance;
var screenWidth = device.widthMeters / eyeToScreenDistance;
var screenHeight = device.heightMeters / eyeToScreenDistance;
var xPxPerTanAngle = device.width / screenWidth;
var yPxPerTanAngle = device.height / screenHeight;
var x = Math.round((p.eyePosX - p.outerDist) * xPxPerTanAngle);
var y = Math.round((p.eyePosY - p.bottomDist) * yPxPerTanAngle);
return {
x: x,
y: y,
width: Math.round((p.eyePosX + p.innerDist) * xPxPerTanAngle) - x,
height: Math.round((p.eyePosY + p.topDist) * yPxPerTanAngle) - y
};
};
DeviceInfo.prototype.getUndistortedParams_ = function() {
var viewer = this.viewer;
var device = this.device;
var distortion = this.distortion;
// Most of these variables in tan-angle units.
var eyeToScreenDistance = viewer.screenLensDistance;
var halfLensDistance = viewer.interLensDistance / 2 / eyeToScreenDistance;
var screenWidth = device.widthMeters / eyeToScreenDistance;
var screenHeight = device.heightMeters / eyeToScreenDistance;
var eyePosX = screenWidth / 2 - halfLensDistance;
var eyePosY = (viewer.baselineLensDistance - device.bevelMeters) / eyeToScreenDistance;
var maxFov = viewer.fov;
var viewerMax = distortion.distortInverse(Math.tan(MathUtil.degToRad * maxFov));
var outerDist = Math.min(eyePosX, viewerMax);
var innerDist = Math.min(halfLensDistance, viewerMax);
var bottomDist = Math.min(eyePosY, viewerMax);
var topDist = Math.min(screenHeight - eyePosY, viewerMax);
return {
outerDist: outerDist,
innerDist: innerDist,
topDist: topDist,
bottomDist: bottomDist,
eyePosX: eyePosX,
eyePosY: eyePosY
};
};
function CardboardViewer(params) {
// A machine readable ID.
this.id = params.id;
// A human readable label.
this.label = params.label;
// Field of view in degrees (per side).
this.fov = params.fov;
// Distance between lens centers in meters.
this.interLensDistance = params.interLensDistance;
// Distance between viewer baseline and lens center in meters.
this.baselineLensDistance = params.baselineLensDistance;
// Screen-to-lens distance in meters.
this.screenLensDistance = params.screenLensDistance;
// Distortion coefficients.
this.distortionCoefficients = params.distortionCoefficients;
// Inverse distortion coefficients.
// TODO: Calculate these from distortionCoefficients in the future.
this.inverseCoefficients = params.inverseCoefficients;
}
// Export viewer information.
DeviceInfo.Viewers = Viewers;
module.exports = DeviceInfo;

90
node_modules/webvr-polyfill/src/display-wrappers.js generated vendored Normal file
View File

@@ -0,0 +1,90 @@
/*
* 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 VRDisplay = require('./base.js').VRDisplay;
var HMDVRDevice = require('./base.js').HMDVRDevice;
var PositionSensorVRDevice = require('./base.js').PositionSensorVRDevice;
/**
* Wraps a VRDisplay and exposes it as a HMDVRDevice
*/
function VRDisplayHMDDevice(display) {
this.display = display;
this.hardwareUnitId = display.displayId;
this.deviceId = 'webvr-polyfill:HMD:' + display.displayId;
this.deviceName = display.displayName + ' (HMD)';
}
VRDisplayHMDDevice.prototype = new HMDVRDevice();
VRDisplayHMDDevice.prototype.getEyeParameters = function(whichEye) {
var eyeParameters = this.display.getEyeParameters(whichEye);
return {
currentFieldOfView: eyeParameters.fieldOfView,
maximumFieldOfView: eyeParameters.fieldOfView,
minimumFieldOfView: eyeParameters.fieldOfView,
recommendedFieldOfView: eyeParameters.fieldOfView,
eyeTranslation: { x: eyeParameters.offset[0], y: eyeParameters.offset[1], z: eyeParameters.offset[2] },
renderRect: {
x: (whichEye == 'right') ? eyeParameters.renderWidth : 0,
y: 0,
width: eyeParameters.renderWidth,
height: eyeParameters.renderHeight
}
};
};
VRDisplayHMDDevice.prototype.setFieldOfView =
function(opt_fovLeft, opt_fovRight, opt_zNear, opt_zFar) {
// Not supported. getEyeParameters reports that the min, max, and recommended
// FoV is all the same, so no adjustment can be made.
};
// TODO: Need to hook requestFullscreen to see if a wrapped VRDisplay was passed
// in as an option. If so we should prevent the default fullscreen behavior and
// call VRDisplay.requestPresent instead.
/**
* Wraps a VRDisplay and exposes it as a PositionSensorVRDevice
*/
function VRDisplayPositionSensorDevice(display) {
this.display = display;
this.hardwareUnitId = display.displayId;
this.deviceId = 'webvr-polyfill:PositionSensor: ' + display.displayId;
this.deviceName = display.displayName + ' (PositionSensor)';
}
VRDisplayPositionSensorDevice.prototype = new PositionSensorVRDevice();
VRDisplayPositionSensorDevice.prototype.getState = function() {
var pose = this.display.getPose();
return {
position: pose.position ? { x: pose.position[0], y: pose.position[1], z: pose.position[2] } : null,
orientation: pose.orientation ? { x: pose.orientation[0], y: pose.orientation[1], z: pose.orientation[2], w: pose.orientation[3] } : null,
linearVelocity: null,
linearAcceleration: null,
angularVelocity: null,
angularAcceleration: null
};
};
VRDisplayPositionSensorDevice.prototype.resetState = function() {
return this.positionDevice.resetPose();
};
module.exports.VRDisplayHMDDevice = VRDisplayHMDDevice;
module.exports.VRDisplayPositionSensorDevice = VRDisplayPositionSensorDevice;

View File

@@ -0,0 +1,181 @@
/**
* TODO(smus): Implement coefficient inversion.
*/
function Distortion(coefficients) {
this.coefficients = coefficients;
}
/**
* Calculates the inverse distortion for a radius.
* </p><p>
* Allows to compute the original undistorted radius from a distorted one.
* See also getApproximateInverseDistortion() for a faster but potentially
* less accurate method.
*
* @param {Number} radius Distorted radius from the lens center in tan-angle units.
* @return {Number} The undistorted radius in tan-angle units.
*/
Distortion.prototype.distortInverse = function(radius) {
// Secant method.
var r0 = 0;
var r1 = 1;
var dr0 = radius - this.distort(r0);
while (Math.abs(r1 - r0) > 0.0001 /** 0.1mm */) {
var dr1 = radius - this.distort(r1);
var r2 = r1 - dr1 * ((r1 - r0) / (dr1 - dr0));
r0 = r1;
r1 = r2;
dr0 = dr1;
}
return r1;
};
/**
* Distorts a radius by its distortion factor from the center of the lenses.
*
* @param {Number} radius Radius from the lens center in tan-angle units.
* @return {Number} The distorted radius in tan-angle units.
*/
Distortion.prototype.distort = function(radius) {
var r2 = radius * radius;
var ret = 0;
for (var i = 0; i < this.coefficients.length; i++) {
ret = r2 * (ret + this.coefficients[i]);
}
return (ret + 1) * radius;
};
// Functions below roughly ported from
// https://github.com/googlesamples/cardboard-unity/blob/master/Cardboard/Scripts/CardboardProfile.cs#L412
// Solves a small linear equation via destructive gaussian
// elimination and back substitution. This isn't generic numeric
// code, it's just a quick hack to work with the generally
// well-behaved symmetric matrices for least-squares fitting.
// Not intended for reuse.
//
// @param a Input positive definite symmetrical matrix. Destroyed
// during calculation.
// @param y Input right-hand-side values. Destroyed during calculation.
// @return Resulting x value vector.
//
Distortion.prototype.solveLinear_ = function(a, y) {
var n = a.length;
// Gaussian elimination (no row exchange) to triangular matrix.
// The input matrix is a A^T A product which should be a positive
// definite symmetrical matrix, and if I remember my linear
// algebra right this implies that the pivots will be nonzero and
// calculations sufficiently accurate without needing row
// exchange.
for (var j = 0; j < n - 1; ++j) {
for (var k = j + 1; k < n; ++k) {
var p = a[j][k] / a[j][j];
for (var i = j + 1; i < n; ++i) {
a[i][k] -= p * a[i][j];
}
y[k] -= p * y[j];
}
}
// From this point on, only the matrix elements a[j][i] with i>=j are
// valid. The elimination doesn't fill in eliminated 0 values.
var x = new Array(n);
// Back substitution.
for (var j = n - 1; j >= 0; --j) {
var v = y[j];
for (var i = j + 1; i < n; ++i) {
v -= a[i][j] * x[i];
}
x[j] = v / a[j][j];
}
return x;
};
// Solves a least-squares matrix equation. Given the equation A * x = y, calculate the
// least-square fit x = inverse(A * transpose(A)) * transpose(A) * y. The way this works
// is that, while A is typically not a square matrix (and hence not invertible), A * transpose(A)
// is always square. That is:
// A * x = y
// transpose(A) * (A * x) = transpose(A) * y <- multiply both sides by transpose(A)
// (transpose(A) * A) * x = transpose(A) * y <- associativity
// x = inverse(transpose(A) * A) * transpose(A) * y <- solve for x
// Matrix A's row count (first index) must match y's value count. A's column count (second index)
// determines the length of the result vector x.
Distortion.prototype.solveLeastSquares_ = function(matA, vecY) {
var i, j, k, sum;
var numSamples = matA.length;
var numCoefficients = matA[0].length;
if (numSamples != vecY.Length) {
throw new Error("Matrix / vector dimension mismatch");
}
// Calculate transpose(A) * A
var matATA = new Array(numCoefficients);
for (k = 0; k < numCoefficients; ++k) {
matATA[k] = new Array(numCoefficients);
for (j = 0; j < numCoefficients; ++j) {
sum = 0;
for (i = 0; i < numSamples; ++i) {
sum += matA[j][i] * matA[k][i];
}
matATA[k][j] = sum;
}
}
// Calculate transpose(A) * y
var vecATY = new Array(numCoefficients);
for (j = 0; j < numCoefficients; ++j) {
sum = 0;
for (i = 0; i < numSamples; ++i) {
sum += matA[j][i] * vecY[i];
}
vecATY[j] = sum;
}
// Now solve (A * transpose(A)) * x = transpose(A) * y.
return this.solveLinear_(matATA, vecATY);
};
/// Calculates an approximate inverse to the given radial distortion parameters.
Distortion.prototype.approximateInverse = function(maxRadius, numSamples) {
maxRadius = maxRadius || 1;
numSamples = numSamples || 100;
var numCoefficients = 6;
var i, j;
// R + K1*R^3 + K2*R^5 = r, with R = rp = distort(r)
// Repeating for numSamples:
// [ R0^3, R0^5 ] * [ K1 ] = [ r0 - R0 ]
// [ R1^3, R1^5 ] [ K2 ] [ r1 - R1 ]
// [ R2^3, R2^5 ] [ r2 - R2 ]
// [ etc... ] [ etc... ]
// That is:
// matA * [K1, K2] = y
// Solve:
// [K1, K2] = inverse(transpose(matA) * matA) * transpose(matA) * y
var matA = new Array(numCoefficients);
for (j = 0; j < numCoefficients; ++j) {
matA[j] = new Array(numSamples);
}
var vecY = new Array(numSamples);
for (i = 0; i < numSamples; ++i) {
var r = maxRadius * (i + 1) / numSamples;
var rp = this.distort(r);
var v = rp;
for (j = 0; j < numCoefficients; ++j) {
v *= rp * rp;
matA[j][i] = v;
}
vecY[i] = r - rp;
}
var inverseCoefficients = this.solveLeastSquares_(matA, vecY);
return new Distortion(inverseCoefficients);
};
module.exports = Distortion;

969
node_modules/webvr-polyfill/src/dpdb/dpdb-cache.js generated vendored Normal file
View File

@@ -0,0 +1,969 @@
/*
* 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.
*/
/**
* DPDB cache.
*/
var DPDB_CACHE = {
"format": 1,
"last_updated": "2016-01-20T00:18:35Z",
"devices": [
{
"type": "android",
"rules": [
{ "mdmh": "asus/*/Nexus 7/*" },
{ "ua": "Nexus 7" }
],
"dpi": [ 320.8, 323.0 ],
"bw": 3,
"ac": 500
},
{
"type": "android",
"rules": [
{ "mdmh": "asus/*/ASUS_Z00AD/*" },
{ "ua": "ASUS_Z00AD" }
],
"dpi": [ 403.0, 404.6 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "HTC/*/HTC6435LVW/*" },
{ "ua": "HTC6435LVW" }
],
"dpi": [ 449.7, 443.3 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "HTC/*/HTC One XL/*" },
{ "ua": "HTC One XL" }
],
"dpi": [ 315.3, 314.6 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "htc/*/Nexus 9/*" },
{ "ua": "Nexus 9" }
],
"dpi": 289.0,
"bw": 3,
"ac": 500
},
{
"type": "android",
"rules": [
{ "mdmh": "HTC/*/HTC One M9/*" },
{ "ua": "HTC One M9" }
],
"dpi": [ 442.5, 443.3 ],
"bw": 3,
"ac": 500
},
{
"type": "android",
"rules": [
{ "mdmh": "HTC/*/HTC One_M8/*" },
{ "ua": "HTC One_M8" }
],
"dpi": [ 449.7, 447.4 ],
"bw": 3,
"ac": 500
},
{
"type": "android",
"rules": [
{ "mdmh": "HTC/*/HTC One/*" },
{ "ua": "HTC One" }
],
"dpi": 472.8,
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "Huawei/*/Nexus 6P/*" },
{ "ua": "Nexus 6P" }
],
"dpi": [ 515.1, 518.0 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "LGE/*/Nexus 5X/*" },
{ "ua": "Nexus 5X" }
],
"dpi": [ 422.0, 419.9 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "LGE/*/LGMS345/*" },
{ "ua": "LGMS345" }
],
"dpi": [ 221.7, 219.1 ],
"bw": 3,
"ac": 500
},
{
"type": "android",
"rules": [
{ "mdmh": "LGE/*/LG-D800/*" },
{ "ua": "LG-D800" }
],
"dpi": [ 422.0, 424.1 ],
"bw": 3,
"ac": 500
},
{
"type": "android",
"rules": [
{ "mdmh": "LGE/*/LG-D850/*" },
{ "ua": "LG-D850" }
],
"dpi": [ 537.9, 541.9 ],
"bw": 3,
"ac": 500
},
{
"type": "android",
"rules": [
{ "mdmh": "LGE/*/VS985 4G/*" },
{ "ua": "VS985 4G" }
],
"dpi": [ 537.9, 535.6 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "LGE/*/Nexus 5/*" },
{ "ua": "Nexus 5 " }
],
"dpi": [ 442.4, 444.8 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "LGE/*/Nexus 4/*" },
{ "ua": "Nexus 4" }
],
"dpi": [ 319.8, 318.4 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "LGE/*/LG-P769/*" },
{ "ua": "LG-P769" }
],
"dpi": [ 240.6, 247.5 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "LGE/*/LGMS323/*" },
{ "ua": "LGMS323" }
],
"dpi": [ 206.6, 204.6 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "LGE/*/LGLS996/*" },
{ "ua": "LGLS996" }
],
"dpi": [ 403.4, 401.5 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "Micromax/*/4560MMX/*" },
{ "ua": "4560MMX" }
],
"dpi": [ 240.0, 219.4 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "Micromax/*/A250/*" },
{ "ua": "Micromax A250" }
],
"dpi": [ 480.0, 446.4 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "Micromax/*/Micromax AQ4501/*" },
{ "ua": "Micromax AQ4501" }
],
"dpi": 240.0,
"bw": 3,
"ac": 500
},
{
"type": "android",
"rules": [
{ "mdmh": "motorola/*/DROID RAZR/*" },
{ "ua": "DROID RAZR" }
],
"dpi": [ 368.1, 256.7 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "motorola/*/XT830C/*" },
{ "ua": "XT830C" }
],
"dpi": [ 254.0, 255.9 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "motorola/*/XT1021/*" },
{ "ua": "XT1021" }
],
"dpi": [ 254.0, 256.7 ],
"bw": 3,
"ac": 500
},
{
"type": "android",
"rules": [
{ "mdmh": "motorola/*/XT1023/*" },
{ "ua": "XT1023" }
],
"dpi": [ 254.0, 256.7 ],
"bw": 3,
"ac": 500
},
{
"type": "android",
"rules": [
{ "mdmh": "motorola/*/XT1028/*" },
{ "ua": "XT1028" }
],
"dpi": [ 326.6, 327.6 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "motorola/*/XT1034/*" },
{ "ua": "XT1034" }
],
"dpi": [ 326.6, 328.4 ],
"bw": 3,
"ac": 500
},
{
"type": "android",
"rules": [
{ "mdmh": "motorola/*/XT1053/*" },
{ "ua": "XT1053" }
],
"dpi": [ 315.3, 316.1 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "motorola/*/XT1562/*" },
{ "ua": "XT1562" }
],
"dpi": [ 403.4, 402.7 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "motorola/*/Nexus 6/*" },
{ "ua": "Nexus 6 " }
],
"dpi": [ 494.3, 489.7 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "motorola/*/XT1063/*" },
{ "ua": "XT1063" }
],
"dpi": [ 295.0, 296.6 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "motorola/*/XT1064/*" },
{ "ua": "XT1064" }
],
"dpi": [ 295.0, 295.6 ],
"bw": 3,
"ac": 500
},
{
"type": "android",
"rules": [
{ "mdmh": "motorola/*/XT1092/*" },
{ "ua": "XT1092" }
],
"dpi": [ 422.0, 424.1 ],
"bw": 3,
"ac": 500
},
{
"type": "android",
"rules": [
{ "mdmh": "motorola/*/XT1095/*" },
{ "ua": "XT1095" }
],
"dpi": [ 422.0, 423.4 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "OnePlus/*/A0001/*" },
{ "ua": "A0001" }
],
"dpi": [ 403.4, 401.0 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "OnePlus/*/ONE E1005/*" },
{ "ua": "ONE E1005" }
],
"dpi": [ 442.4, 441.4 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "OnePlus/*/ONE A2005/*" },
{ "ua": "ONE A2005" }
],
"dpi": [ 391.9, 405.4 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "OPPO/*/X909/*" },
{ "ua": "X909" }
],
"dpi": [ 442.4, 444.1 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "samsung/*/GT-I9082/*" },
{ "ua": "GT-I9082" }
],
"dpi": [ 184.7, 185.4 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "samsung/*/SM-G360P/*" },
{ "ua": "SM-G360P" }
],
"dpi": [ 196.7, 205.4 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "samsung/*/Nexus S/*" },
{ "ua": "Nexus S" }
],
"dpi": [ 234.5, 229.8 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "samsung/*/GT-I9300/*" },
{ "ua": "GT-I9300" }
],
"dpi": [ 304.8, 303.9 ],
"bw": 5,
"ac": 500
},
{
"type": "android",
"rules": [
{ "mdmh": "samsung/*/SM-T230NU/*" },
{ "ua": "SM-T230NU" }
],
"dpi": 216.0,
"bw": 3,
"ac": 500
},
{
"type": "android",
"rules": [
{ "mdmh": "samsung/*/SGH-T399/*" },
{ "ua": "SGH-T399" }
],
"dpi": [ 217.7, 231.4 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "samsung/*/SM-N9005/*" },
{ "ua": "SM-N9005" }
],
"dpi": [ 386.4, 387.0 ],
"bw": 3,
"ac": 500
},
{
"type": "android",
"rules": [
{ "mdmh": "samsung/*/SAMSUNG-SM-N900A/*" },
{ "ua": "SAMSUNG-SM-N900A" }
],
"dpi": [ 386.4, 387.7 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "samsung/*/GT-I9500/*" },
{ "ua": "GT-I9500" }
],
"dpi": [ 442.5, 443.3 ],
"bw": 3,
"ac": 500
},
{
"type": "android",
"rules": [
{ "mdmh": "samsung/*/GT-I9505/*" },
{ "ua": "GT-I9505" }
],
"dpi": 439.4,
"bw": 4,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "samsung/*/SM-G900F/*" },
{ "ua": "SM-G900F" }
],
"dpi": [ 415.6, 431.6 ],
"bw": 5,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "samsung/*/SM-G900M/*" },
{ "ua": "SM-G900M" }
],
"dpi": [ 415.6, 431.6 ],
"bw": 5,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "samsung/*/SM-G800F/*" },
{ "ua": "SM-G800F" }
],
"dpi": 326.8,
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "samsung/*/SM-G906S/*" },
{ "ua": "SM-G906S" }
],
"dpi": [ 562.7, 572.4 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "samsung/*/GT-I9300/*" },
{ "ua": "GT-I9300" }
],
"dpi": [ 306.7, 304.8 ],
"bw": 5,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "samsung/*/SM-T535/*" },
{ "ua": "SM-T535" }
],
"dpi": [ 142.6, 136.4 ],
"bw": 3,
"ac": 500
},
{
"type": "android",
"rules": [
{ "mdmh": "samsung/*/SM-N920C/*" },
{ "ua": "SM-N920C" }
],
"dpi": [ 515.1, 518.4 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "samsung/*/GT-I9300I/*" },
{ "ua": "GT-I9300I" }
],
"dpi": [ 304.8, 305.8 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "samsung/*/GT-I9195/*" },
{ "ua": "GT-I9195" }
],
"dpi": [ 249.4, 256.7 ],
"bw": 3,
"ac": 500
},
{
"type": "android",
"rules": [
{ "mdmh": "samsung/*/SPH-L520/*" },
{ "ua": "SPH-L520" }
],
"dpi": [ 249.4, 255.9 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "samsung/*/SAMSUNG-SGH-I717/*" },
{ "ua": "SAMSUNG-SGH-I717" }
],
"dpi": 285.8,
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "samsung/*/SPH-D710/*" },
{ "ua": "SPH-D710" }
],
"dpi": [ 217.7, 204.2 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "samsung/*/GT-N7100/*" },
{ "ua": "GT-N7100" }
],
"dpi": 265.1,
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "samsung/*/SCH-I605/*" },
{ "ua": "SCH-I605" }
],
"dpi": 265.1,
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "samsung/*/Galaxy Nexus/*" },
{ "ua": "Galaxy Nexus" }
],
"dpi": [ 315.3, 314.2 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "samsung/*/SM-N910H/*" },
{ "ua": "SM-N910H" }
],
"dpi": [ 515.1, 518.0 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "samsung/*/SM-N910C/*" },
{ "ua": "SM-N910C" }
],
"dpi": [ 515.2, 520.2 ],
"bw": 3,
"ac": 500
},
{
"type": "android",
"rules": [
{ "mdmh": "samsung/*/SM-G130M/*" },
{ "ua": "SM-G130M" }
],
"dpi": [ 165.9, 164.8 ],
"bw": 3,
"ac": 500
},
{
"type": "android",
"rules": [
{ "mdmh": "samsung/*/SM-G928I/*" },
{ "ua": "SM-G928I" }
],
"dpi": [ 515.1, 518.4 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "samsung/*/SM-G920F/*" },
{ "ua": "SM-G920F" }
],
"dpi": 580.6,
"bw": 3,
"ac": 500
},
{
"type": "android",
"rules": [
{ "mdmh": "samsung/*/SM-G920P/*" },
{ "ua": "SM-G920P" }
],
"dpi": [ 522.5, 577.0 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "samsung/*/SM-G925F/*" },
{ "ua": "SM-G925F" }
],
"dpi": 580.6,
"bw": 3,
"ac": 500
},
{
"type": "android",
"rules": [
{ "mdmh": "samsung/*/SM-G925V/*" },
{ "ua": "SM-G925V" }
],
"dpi": [ 522.5, 576.6 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "Sony/*/C6903/*" },
{ "ua": "C6903" }
],
"dpi": [ 442.5, 443.3 ],
"bw": 3,
"ac": 500
},
{
"type": "android",
"rules": [
{ "mdmh": "Sony/*/D6653/*" },
{ "ua": "D6653" }
],
"dpi": [ 428.6, 427.6 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "Sony/*/E6653/*" },
{ "ua": "E6653" }
],
"dpi": [ 428.6, 425.7 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "Sony/*/E6853/*" },
{ "ua": "E6853" }
],
"dpi": [ 403.4, 401.9 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "Sony/*/SGP321/*" },
{ "ua": "SGP321" }
],
"dpi": [ 224.7, 224.1 ],
"bw": 3,
"ac": 500
},
{
"type": "android",
"rules": [
{ "mdmh": "TCT/*/ALCATEL ONE TOUCH Fierce/*" },
{ "ua": "ALCATEL ONE TOUCH Fierce" }
],
"dpi": [ 240.0, 247.5 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "THL/*/thl 5000/*" },
{ "ua": "thl 5000" }
],
"dpi": [ 480.0, 443.3 ],
"bw": 3,
"ac": 1000
},
{
"type": "android",
"rules": [
{ "mdmh": "ZTE/*/ZTE Blade L2/*" },
{ "ua": "ZTE Blade L2" }
],
"dpi": 240.0,
"bw": 3,
"ac": 500
},
{
"type": "ios",
"rules": [ { "res": [ 640, 960 ] } ],
"dpi": [ 325.1, 328.4 ],
"bw": 4,
"ac": 1000
},
{
"type": "ios",
"rules": [ { "res": [ 640, 960 ] } ],
"dpi": [ 325.1, 328.4 ],
"bw": 4,
"ac": 1000
},
{
"type": "ios",
"rules": [ { "res": [ 640, 1136 ] } ],
"dpi": [ 317.1, 320.2 ],
"bw": 3,
"ac": 1000
},
{
"type": "ios",
"rules": [ { "res": [ 640, 1136 ] } ],
"dpi": [ 317.1, 320.2 ],
"bw": 3,
"ac": 1000
},
{
"type": "ios",
"rules": [ { "res": [ 750, 1334 ] } ],
"dpi": 326.4,
"bw": 4,
"ac": 1000
},
{
"type": "ios",
"rules": [ { "res": [ 750, 1334 ] } ],
"dpi": 326.4,
"bw": 4,
"ac": 1000
},
{
"type": "ios",
"rules": [ { "res": [ 1242, 2208 ] } ],
"dpi": [ 453.6, 458.4 ],
"bw": 4,
"ac": 1000
},
{
"type": "ios",
"rules": [ { "res": [ 1242, 2208 ] } ],
"dpi": [ 453.6, 458.4 ],
"bw": 4,
"ac": 1000
}
]};
module.exports = DPDB_CACHE;

179
node_modules/webvr-polyfill/src/dpdb/dpdb.js generated vendored Normal file
View File

@@ -0,0 +1,179 @@
/*
* 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.
*/
// Offline cache of the DPDB, to be used until we load the online one (and
// as a fallback in case we can't load the online one).
var DPDB_CACHE = require('./dpdb-cache.js');
var Util = require('../util.js');
// Online DPDB URL.
var ONLINE_DPDB_URL = 'https://storage.googleapis.com/cardboard-dpdb/dpdb.json';
/**
* Calculates device parameters based on the DPDB (Device Parameter Database).
* Initially, uses the cached DPDB values.
*
* If fetchOnline == true, then this object tries to fetch the online version
* of the DPDB and updates the device info if a better match is found.
* Calls the onDeviceParamsUpdated callback when there is an update to the
* device information.
*/
function Dpdb(fetchOnline, onDeviceParamsUpdated) {
// Start with the offline DPDB cache while we are loading the real one.
this.dpdb = DPDB_CACHE;
// Calculate device params based on the offline version of the DPDB.
this.recalculateDeviceParams_();
// XHR to fetch online DPDB file, if requested.
if (fetchOnline) {
// Set the callback.
this.onDeviceParamsUpdated = onDeviceParamsUpdated;
var xhr = new XMLHttpRequest();
var obj = this;
xhr.open('GET', ONLINE_DPDB_URL, true);
xhr.addEventListener('load', function() {
obj.loading = false;
if (xhr.status >= 200 && xhr.status <= 299) {
// Success.
obj.dpdb = JSON.parse(xhr.response);
obj.recalculateDeviceParams_();
} else {
// Error loading the DPDB.
console.error('Error loading online DPDB!');
}
});
xhr.send();
}
}
// Returns the current device parameters.
Dpdb.prototype.getDeviceParams = function() {
return this.deviceParams;
};
// Recalculates this device's parameters based on the DPDB.
Dpdb.prototype.recalculateDeviceParams_ = function() {
var newDeviceParams = this.calcDeviceParams_();
if (newDeviceParams) {
this.deviceParams = newDeviceParams;
// Invoke callback, if it is set.
if (this.onDeviceParamsUpdated) {
this.onDeviceParamsUpdated(this.deviceParams);
}
} else {
console.error('Failed to recalculate device parameters.');
}
};
// Returns a DeviceParams object that represents the best guess as to this
// device's parameters. Can return null if the device does not match any
// known devices.
Dpdb.prototype.calcDeviceParams_ = function() {
var db = this.dpdb; // shorthand
if (!db) {
console.error('DPDB not available.');
return null;
}
if (db.format != 1) {
console.error('DPDB has unexpected format version.');
return null;
}
if (!db.devices || !db.devices.length) {
console.error('DPDB does not have a devices section.');
return null;
}
// Get the actual user agent and screen dimensions in pixels.
var userAgent = navigator.userAgent || navigator.vendor || window.opera;
var width = Util.getScreenWidth();
var height = Util.getScreenHeight();
if (!db.devices) {
console.error('DPDB has no devices section.');
return null;
}
for (var i = 0; i < db.devices.length; i++) {
var device = db.devices[i];
if (!device.rules) {
console.warn('Device[' + i + '] has no rules section.');
continue;
}
if (device.type != 'ios' && device.type != 'android') {
console.warn('Device[' + i + '] has invalid type.');
continue;
}
// See if this device is of the appropriate type.
if (Util.isIOS() != (device.type == 'ios')) continue;
// See if this device matches any of the rules:
var matched = false;
for (var j = 0; j < device.rules.length; j++) {
var rule = device.rules[j];
if (this.matchRule_(rule, userAgent, width, height)) {
matched = true;
break;
}
}
if (!matched) continue;
// device.dpi might be an array of [ xdpi, ydpi] or just a scalar.
var xdpi = device.dpi[0] || device.dpi;
var ydpi = device.dpi[1] || device.dpi;
return new DeviceParams({ xdpi: xdpi, ydpi: ydpi, bevelMm: device.bw });
}
console.warn('No DPDB device match.');
return null;
};
Dpdb.prototype.matchRule_ = function(rule, ua, screenWidth, screenHeight) {
// We can only match 'ua' and 'res' rules, not other types like 'mdmh'
// (which are meant for native platforms).
if (!rule.ua && !rule.res) return false;
// If our user agent string doesn't contain the indicated user agent string,
// the match fails.
if (rule.ua && ua.indexOf(rule.ua) < 0) return false;
// If the rule specifies screen dimensions that don't correspond to ours,
// the match fails.
if (rule.res) {
if (!rule.res[0] || !rule.res[1]) return false;
var resX = rule.res[0];
var resY = rule.res[1];
// Compare min and max so as to make the order not matter, i.e., it should
// be true that 640x480 == 480x640.
if (Math.min(screenWidth, screenHeight) != Math.min(resX, resY) ||
(Math.max(screenWidth, screenHeight) != Math.max(resX, resY))) {
return false;
}
}
return true;
}
function DeviceParams(params) {
this.xdpi = params.xdpi;
this.ydpi = params.ydpi;
this.bevelMm = params.bevelMm;
}
module.exports = Dpdb;

80
node_modules/webvr-polyfill/src/main.js generated vendored Normal file
View File

@@ -0,0 +1,80 @@
/*
* 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 WebVRPolyfill = require('./webvr-polyfill.js').WebVRPolyfill;
// Initialize a WebVRConfig just in case.
window.WebVRConfig = Util.extend({
// Forces availability of VR mode, even for non-mobile devices.
FORCE_ENABLE_VR: false,
// Complementary filter coefficient. 0 for accelerometer, 1 for gyro.
K_FILTER: 0.98,
// How far into the future to predict during fast motion (in seconds).
PREDICTION_TIME_S: 0.040,
// Flag to enable touch panner. In case you have your own touch controls.
TOUCH_PANNER_DISABLED: true,
// Flag to disabled the UI in VR Mode.
CARDBOARD_UI_DISABLED: false, // Default: false
// Flag to disable the instructions to rotate your device.
ROTATE_INSTRUCTIONS_DISABLED: false, // Default: false.
// Enable yaw panning only, disabling roll and pitch. This can be useful
// for panoramas with nothing interesting above or below.
YAW_ONLY: false,
// To disable keyboard and mouse controls, if you want to use your own
// implementation.
MOUSE_KEYBOARD_CONTROLS_DISABLED: false,
// Prevent the polyfill from initializing immediately. Requires the app
// to call InitializeWebVRPolyfill() before it can be used.
DEFER_INITIALIZATION: false,
// Enable the deprecated version of the API (navigator.getVRDevices).
ENABLE_DEPRECATED_API: false,
// Scales the recommended buffer size reported by WebVR, which can improve
// performance.
// UPDATE(2016-05-03): Setting this to 0.5 by default since 1.0 does not
// perform well on many mobile devices.
BUFFER_SCALE: 0.5,
// Allow VRDisplay.submitFrame to change gl bindings, which is more
// efficient if the application code will re-bind its resources on the
// next frame anyway. This has been seen to cause rendering glitches with
// THREE.js.
// Dirty bindings include: gl.FRAMEBUFFER_BINDING, gl.CURRENT_PROGRAM,
// gl.ARRAY_BUFFER_BINDING, gl.ELEMENT_ARRAY_BUFFER_BINDING,
// and gl.TEXTURE_BINDING_2D for texture unit 0.
DIRTY_SUBMIT_FRAME_BINDINGS: false,
// When set to true, this will cause a polyfilled VRDisplay to always be
// appended to the list returned by navigator.getVRDisplays(), even if that
// list includes a native VRDisplay.
ALWAYS_APPEND_POLYFILL_DISPLAY: false
}, window.WebVRConfig);
if (!window.WebVRConfig.DEFER_INITIALIZATION) {
new WebVRPolyfill();
} else {
window.InitializeWebVRPolyfill = function() {
new WebVRPolyfill();
}
}

357
node_modules/webvr-polyfill/src/math-util.js generated vendored Normal file
View File

@@ -0,0 +1,357 @@
/*
* 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 MathUtil = window.MathUtil || {};
MathUtil.degToRad = Math.PI / 180;
MathUtil.radToDeg = 180 / Math.PI;
// Some minimal math functionality borrowed from THREE.Math and stripped down
// for the purposes of this library.
MathUtil.Vector2 = function ( x, y ) {
this.x = x || 0;
this.y = y || 0;
};
MathUtil.Vector2.prototype = {
constructor: MathUtil.Vector2,
set: function ( x, y ) {
this.x = x;
this.y = y;
return this;
},
copy: function ( v ) {
this.x = v.x;
this.y = v.y;
return this;
},
subVectors: function ( a, b ) {
this.x = a.x - b.x;
this.y = a.y - b.y;
return this;
},
};
MathUtil.Vector3 = function ( x, y, z ) {
this.x = x || 0;
this.y = y || 0;
this.z = z || 0;
};
MathUtil.Vector3.prototype = {
constructor: MathUtil.Vector3,
set: function ( x, y, z ) {
this.x = x;
this.y = y;
this.z = z;
return this;
},
copy: function ( v ) {
this.x = v.x;
this.y = v.y;
this.z = v.z;
return this;
},
length: function () {
return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z );
},
normalize: function () {
var scalar = this.length();
if ( scalar !== 0 ) {
var invScalar = 1 / scalar;
this.multiplyScalar(invScalar);
} else {
this.x = 0;
this.y = 0;
this.z = 0;
}
return this;
},
multiplyScalar: function ( scalar ) {
this.x *= scalar;
this.y *= scalar;
this.z *= scalar;
},
applyQuaternion: function ( q ) {
var x = this.x;
var y = this.y;
var z = this.z;
var qx = q.x;
var qy = q.y;
var qz = q.z;
var qw = q.w;
// calculate quat * vector
var ix = qw * x + qy * z - qz * y;
var iy = qw * y + qz * x - qx * z;
var iz = qw * z + qx * y - qy * x;
var iw = - qx * x - qy * y - qz * z;
// calculate result * inverse quat
this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy;
this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz;
this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx;
return this;
},
dot: function ( v ) {
return this.x * v.x + this.y * v.y + this.z * v.z;
},
crossVectors: function ( a, b ) {
var ax = a.x, ay = a.y, az = a.z;
var bx = b.x, by = b.y, bz = b.z;
this.x = ay * bz - az * by;
this.y = az * bx - ax * bz;
this.z = ax * by - ay * bx;
return this;
},
};
MathUtil.Quaternion = function ( x, y, z, w ) {
this.x = x || 0;
this.y = y || 0;
this.z = z || 0;
this.w = ( w !== undefined ) ? w : 1;
};
MathUtil.Quaternion.prototype = {
constructor: MathUtil.Quaternion,
set: function ( x, y, z, w ) {
this.x = x;
this.y = y;
this.z = z;
this.w = w;
return this;
},
copy: function ( quaternion ) {
this.x = quaternion.x;
this.y = quaternion.y;
this.z = quaternion.z;
this.w = quaternion.w;
return this;
},
setFromEulerXYZ: function( x, y, z ) {
var c1 = Math.cos( x / 2 );
var c2 = Math.cos( y / 2 );
var c3 = Math.cos( z / 2 );
var s1 = Math.sin( x / 2 );
var s2 = Math.sin( y / 2 );
var s3 = Math.sin( z / 2 );
this.x = s1 * c2 * c3 + c1 * s2 * s3;
this.y = c1 * s2 * c3 - s1 * c2 * s3;
this.z = c1 * c2 * s3 + s1 * s2 * c3;
this.w = c1 * c2 * c3 - s1 * s2 * s3;
return this;
},
setFromEulerYXZ: function( x, y, z ) {
var c1 = Math.cos( x / 2 );
var c2 = Math.cos( y / 2 );
var c3 = Math.cos( z / 2 );
var s1 = Math.sin( x / 2 );
var s2 = Math.sin( y / 2 );
var s3 = Math.sin( z / 2 );
this.x = s1 * c2 * c3 + c1 * s2 * s3;
this.y = c1 * s2 * c3 - s1 * c2 * s3;
this.z = c1 * c2 * s3 - s1 * s2 * c3;
this.w = c1 * c2 * c3 + s1 * s2 * s3;
return this;
},
setFromAxisAngle: function ( axis, angle ) {
// http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm
// assumes axis is normalized
var halfAngle = angle / 2, s = Math.sin( halfAngle );
this.x = axis.x * s;
this.y = axis.y * s;
this.z = axis.z * s;
this.w = Math.cos( halfAngle );
return this;
},
multiply: function ( q ) {
return this.multiplyQuaternions( this, q );
},
multiplyQuaternions: function ( a, b ) {
// from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm
var qax = a.x, qay = a.y, qaz = a.z, qaw = a.w;
var qbx = b.x, qby = b.y, qbz = b.z, qbw = b.w;
this.x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby;
this.y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz;
this.z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx;
this.w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz;
return this;
},
inverse: function () {
this.x *= -1;
this.y *= -1;
this.z *= -1;
this.normalize();
return this;
},
normalize: function () {
var l = Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w );
if ( l === 0 ) {
this.x = 0;
this.y = 0;
this.z = 0;
this.w = 1;
} else {
l = 1 / l;
this.x = this.x * l;
this.y = this.y * l;
this.z = this.z * l;
this.w = this.w * l;
}
return this;
},
slerp: function ( qb, t ) {
if ( t === 0 ) return this;
if ( t === 1 ) return this.copy( qb );
var x = this.x, y = this.y, z = this.z, w = this.w;
// http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/
var cosHalfTheta = w * qb.w + x * qb.x + y * qb.y + z * qb.z;
if ( cosHalfTheta < 0 ) {
this.w = - qb.w;
this.x = - qb.x;
this.y = - qb.y;
this.z = - qb.z;
cosHalfTheta = - cosHalfTheta;
} else {
this.copy( qb );
}
if ( cosHalfTheta >= 1.0 ) {
this.w = w;
this.x = x;
this.y = y;
this.z = z;
return this;
}
var halfTheta = Math.acos( cosHalfTheta );
var sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta );
if ( Math.abs( sinHalfTheta ) < 0.001 ) {
this.w = 0.5 * ( w + this.w );
this.x = 0.5 * ( x + this.x );
this.y = 0.5 * ( y + this.y );
this.z = 0.5 * ( z + this.z );
return this;
}
var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta,
ratioB = Math.sin( t * halfTheta ) / sinHalfTheta;
this.w = ( w * ratioA + this.w * ratioB );
this.x = ( x * ratioA + this.x * ratioB );
this.y = ( y * ratioA + this.y * ratioB );
this.z = ( z * ratioA + this.z * ratioB );
return this;
},
setFromUnitVectors: function () {
// http://lolengine.net/blog/2014/02/24/quaternion-from-two-vectors-final
// assumes direction vectors vFrom and vTo are normalized
var v1, r;
var EPS = 0.000001;
return function ( vFrom, vTo ) {
if ( v1 === undefined ) v1 = new MathUtil.Vector3();
r = vFrom.dot( vTo ) + 1;
if ( r < EPS ) {
r = 0;
if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) {
v1.set( - vFrom.y, vFrom.x, 0 );
} else {
v1.set( 0, - vFrom.z, vFrom.y );
}
} else {
v1.crossVectors( vFrom, vTo );
}
this.x = v1.x;
this.y = v1.y;
this.z = v1.z;
this.w = r;
this.normalize();
return this;
}
}(),
};
module.exports = MathUtil;

View File

@@ -0,0 +1,177 @@
/*
* 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 VRDisplay = require('./base.js').VRDisplay;
var MathUtil = require('./math-util.js');
var Util = require('./util.js');
// How much to rotate per key stroke.
var KEY_SPEED = 0.15;
var KEY_ANIMATION_DURATION = 80;
// How much to rotate for mouse events.
var MOUSE_SPEED_X = 0.5;
var MOUSE_SPEED_Y = 0.3;
/**
* VRDisplay based on mouse and keyboard input. Designed for desktops/laptops
* where orientation events aren't supported. Cannot present.
*/
function MouseKeyboardVRDisplay() {
this.displayName = 'Mouse and Keyboard VRDisplay (webvr-polyfill)';
this.capabilities.hasOrientation = true;
// Attach to mouse and keyboard events.
window.addEventListener('keydown', this.onKeyDown_.bind(this));
window.addEventListener('mousemove', this.onMouseMove_.bind(this));
window.addEventListener('mousedown', this.onMouseDown_.bind(this));
window.addEventListener('mouseup', this.onMouseUp_.bind(this));
// "Private" members.
this.phi_ = 0;
this.theta_ = 0;
// Variables for keyboard-based rotation animation.
this.targetAngle_ = null;
this.angleAnimation_ = null;
// State variables for calculations.
this.orientation_ = new MathUtil.Quaternion();
// Variables for mouse-based rotation.
this.rotateStart_ = new MathUtil.Vector2();
this.rotateEnd_ = new MathUtil.Vector2();
this.rotateDelta_ = new MathUtil.Vector2();
this.isDragging_ = false;
this.orientationOut_ = new Float32Array(4);
}
MouseKeyboardVRDisplay.prototype = new VRDisplay();
MouseKeyboardVRDisplay.prototype.getImmediatePose = function() {
this.orientation_.setFromEulerYXZ(this.phi_, this.theta_, 0);
this.orientationOut_[0] = this.orientation_.x;
this.orientationOut_[1] = this.orientation_.y;
this.orientationOut_[2] = this.orientation_.z;
this.orientationOut_[3] = this.orientation_.w;
return {
position: null,
orientation: this.orientationOut_,
linearVelocity: null,
linearAcceleration: null,
angularVelocity: null,
angularAcceleration: null
};
};
MouseKeyboardVRDisplay.prototype.onKeyDown_ = function(e) {
// Track WASD and arrow keys.
if (e.keyCode == 38) { // Up key.
this.animatePhi_(this.phi_ + KEY_SPEED);
} else if (e.keyCode == 39) { // Right key.
this.animateTheta_(this.theta_ - KEY_SPEED);
} else if (e.keyCode == 40) { // Down key.
this.animatePhi_(this.phi_ - KEY_SPEED);
} else if (e.keyCode == 37) { // Left key.
this.animateTheta_(this.theta_ + KEY_SPEED);
}
};
MouseKeyboardVRDisplay.prototype.animateTheta_ = function(targetAngle) {
this.animateKeyTransitions_('theta_', targetAngle);
};
MouseKeyboardVRDisplay.prototype.animatePhi_ = function(targetAngle) {
// Prevent looking too far up or down.
targetAngle = Util.clamp(targetAngle, -Math.PI/2, Math.PI/2);
this.animateKeyTransitions_('phi_', targetAngle);
};
/**
* Start an animation to transition an angle from one value to another.
*/
MouseKeyboardVRDisplay.prototype.animateKeyTransitions_ = function(angleName, targetAngle) {
// If an animation is currently running, cancel it.
if (this.angleAnimation_) {
cancelAnimationFrame(this.angleAnimation_);
}
var startAngle = this[angleName];
var startTime = new Date();
// Set up an interval timer to perform the animation.
this.angleAnimation_ = requestAnimationFrame(function animate() {
// Once we're finished the animation, we're done.
var elapsed = new Date() - startTime;
if (elapsed >= KEY_ANIMATION_DURATION) {
this[angleName] = targetAngle;
cancelAnimationFrame(this.angleAnimation_);
return;
}
// loop with requestAnimationFrame
this.angleAnimation_ = requestAnimationFrame(animate.bind(this))
// Linearly interpolate the angle some amount.
var percent = elapsed / KEY_ANIMATION_DURATION;
this[angleName] = startAngle + (targetAngle - startAngle) * percent;
}.bind(this));
};
MouseKeyboardVRDisplay.prototype.onMouseDown_ = function(e) {
this.rotateStart_.set(e.clientX, e.clientY);
this.isDragging_ = true;
};
// Very similar to https://gist.github.com/mrflix/8351020
MouseKeyboardVRDisplay.prototype.onMouseMove_ = function(e) {
if (!this.isDragging_ && !this.isPointerLocked_()) {
return;
}
// Support pointer lock API.
if (this.isPointerLocked_()) {
var movementX = e.movementX || e.mozMovementX || 0;
var movementY = e.movementY || e.mozMovementY || 0;
this.rotateEnd_.set(this.rotateStart_.x - movementX, this.rotateStart_.y - movementY);
} else {
this.rotateEnd_.set(e.clientX, e.clientY);
}
// Calculate how much we moved in mouse space.
this.rotateDelta_.subVectors(this.rotateEnd_, this.rotateStart_);
this.rotateStart_.copy(this.rotateEnd_);
// Keep track of the cumulative euler angles.
this.phi_ += 2 * Math.PI * this.rotateDelta_.y / screen.height * MOUSE_SPEED_Y;
this.theta_ += 2 * Math.PI * this.rotateDelta_.x / screen.width * MOUSE_SPEED_X;
// Prevent looking too far up or down.
this.phi_ = Util.clamp(this.phi_, -Math.PI/2, Math.PI/2);
};
MouseKeyboardVRDisplay.prototype.onMouseUp_ = function(e) {
this.isDragging_ = false;
};
MouseKeyboardVRDisplay.prototype.isPointerLocked_ = function() {
var el = document.pointerLockElement || document.mozPointerLockElement ||
document.webkitPointerLockElement;
return el !== undefined;
};
MouseKeyboardVRDisplay.prototype.resetPose = function() {
this.phi_ = 0;
this.theta_ = 0;
};
module.exports = MouseKeyboardVRDisplay;

144
node_modules/webvr-polyfill/src/rotate-instructions.js generated vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,166 @@
/*
* 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 SensorSample = require('./sensor-sample.js');
var MathUtil = require('../math-util.js');
var Util = require('../util.js');
/**
* An implementation of a simple complementary filter, which fuses gyroscope and
* accelerometer data from the 'devicemotion' event.
*
* Accelerometer data is very noisy, but stable over the long term.
* Gyroscope data is smooth, but tends to drift over the long term.
*
* This fusion is relatively simple:
* 1. Get orientation estimates from accelerometer by applying a low-pass filter
* on that data.
* 2. Get orientation estimates from gyroscope by integrating over time.
* 3. Combine the two estimates, weighing (1) in the long term, but (2) for the
* short term.
*/
function ComplementaryFilter(kFilter) {
this.kFilter = kFilter;
// Raw sensor measurements.
this.currentAccelMeasurement = new SensorSample();
this.currentGyroMeasurement = new SensorSample();
this.previousGyroMeasurement = new SensorSample();
// Set default look direction to be in the correct direction.
if (Util.isIOS()) {
this.filterQ = new MathUtil.Quaternion(-1, 0, 0, 1);
} else {
this.filterQ = new MathUtil.Quaternion(1, 0, 0, 1);
}
this.previousFilterQ = new MathUtil.Quaternion();
this.previousFilterQ.copy(this.filterQ);
// Orientation based on the accelerometer.
this.accelQ = new MathUtil.Quaternion();
// Whether or not the orientation has been initialized.
this.isOrientationInitialized = false;
// Running estimate of gravity based on the current orientation.
this.estimatedGravity = new MathUtil.Vector3();
// Measured gravity based on accelerometer.
this.measuredGravity = new MathUtil.Vector3();
// Debug only quaternion of gyro-based orientation.
this.gyroIntegralQ = new MathUtil.Quaternion();
}
ComplementaryFilter.prototype.addAccelMeasurement = function(vector, timestampS) {
this.currentAccelMeasurement.set(vector, timestampS);
};
ComplementaryFilter.prototype.addGyroMeasurement = function(vector, timestampS) {
this.currentGyroMeasurement.set(vector, timestampS);
var deltaT = timestampS - this.previousGyroMeasurement.timestampS;
if (Util.isTimestampDeltaValid(deltaT)) {
this.run_();
}
this.previousGyroMeasurement.copy(this.currentGyroMeasurement);
};
ComplementaryFilter.prototype.run_ = function() {
if (!this.isOrientationInitialized) {
this.accelQ = this.accelToQuaternion_(this.currentAccelMeasurement.sample);
this.previousFilterQ.copy(this.accelQ);
this.isOrientationInitialized = true;
return;
}
var deltaT = this.currentGyroMeasurement.timestampS -
this.previousGyroMeasurement.timestampS;
// Convert gyro rotation vector to a quaternion delta.
var gyroDeltaQ = this.gyroToQuaternionDelta_(this.currentGyroMeasurement.sample, deltaT);
this.gyroIntegralQ.multiply(gyroDeltaQ);
// filter_1 = K * (filter_0 + gyro * dT) + (1 - K) * accel.
this.filterQ.copy(this.previousFilterQ);
this.filterQ.multiply(gyroDeltaQ);
// Calculate the delta between the current estimated gravity and the real
// gravity vector from accelerometer.
var invFilterQ = new MathUtil.Quaternion();
invFilterQ.copy(this.filterQ);
invFilterQ.inverse();
this.estimatedGravity.set(0, 0, -1);
this.estimatedGravity.applyQuaternion(invFilterQ);
this.estimatedGravity.normalize();
this.measuredGravity.copy(this.currentAccelMeasurement.sample);
this.measuredGravity.normalize();
// Compare estimated gravity with measured gravity, get the delta quaternion
// between the two.
var deltaQ = new MathUtil.Quaternion();
deltaQ.setFromUnitVectors(this.estimatedGravity, this.measuredGravity);
deltaQ.inverse();
if (Util.isDebug()) {
console.log('Delta: %d deg, G_est: (%s, %s, %s), G_meas: (%s, %s, %s)',
MathUtil.radToDeg * Util.getQuaternionAngle(deltaQ),
(this.estimatedGravity.x).toFixed(1),
(this.estimatedGravity.y).toFixed(1),
(this.estimatedGravity.z).toFixed(1),
(this.measuredGravity.x).toFixed(1),
(this.measuredGravity.y).toFixed(1),
(this.measuredGravity.z).toFixed(1));
}
// Calculate the SLERP target: current orientation plus the measured-estimated
// quaternion delta.
var targetQ = new MathUtil.Quaternion();
targetQ.copy(this.filterQ);
targetQ.multiply(deltaQ);
// SLERP factor: 0 is pure gyro, 1 is pure accel.
this.filterQ.slerp(targetQ, 1 - this.kFilter);
this.previousFilterQ.copy(this.filterQ);
};
ComplementaryFilter.prototype.getOrientation = function() {
return this.filterQ;
};
ComplementaryFilter.prototype.accelToQuaternion_ = function(accel) {
var normAccel = new MathUtil.Vector3();
normAccel.copy(accel);
normAccel.normalize();
var quat = new MathUtil.Quaternion();
quat.setFromUnitVectors(new MathUtil.Vector3(0, 0, -1), normAccel);
quat.inverse();
return quat;
};
ComplementaryFilter.prototype.gyroToQuaternionDelta_ = function(gyro, dt) {
// Extract axis and angle from the gyroscope data.
var quat = new MathUtil.Quaternion();
var axis = new MathUtil.Vector3();
axis.copy(gyro);
axis.normalize();
quat.setFromAxisAngle(axis, gyro.length() * dt);
return quat;
};
module.exports = ComplementaryFilter;

View File

@@ -0,0 +1,229 @@
/*
* 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 ComplementaryFilter = require('./complementary-filter.js');
var PosePredictor = require('./pose-predictor.js');
var TouchPanner = require('../touch-panner.js');
var MathUtil = require('../math-util.js');
var Util = require('../util.js');
/**
* The pose sensor, implemented using DeviceMotion APIs.
*/
function FusionPoseSensor() {
this.deviceId = 'webvr-polyfill:fused';
this.deviceName = 'VR Position Device (webvr-polyfill:fused)';
this.accelerometer = new MathUtil.Vector3();
this.gyroscope = new MathUtil.Vector3();
this.start();
this.filter = new ComplementaryFilter(WebVRConfig.K_FILTER);
this.posePredictor = new PosePredictor(WebVRConfig.PREDICTION_TIME_S);
this.touchPanner = new TouchPanner();
this.filterToWorldQ = new MathUtil.Quaternion();
// Set the filter to world transform, depending on OS.
if (Util.isIOS()) {
this.filterToWorldQ.setFromAxisAngle(new MathUtil.Vector3(1, 0, 0), Math.PI / 2);
} else {
this.filterToWorldQ.setFromAxisAngle(new MathUtil.Vector3(1, 0, 0), -Math.PI / 2);
}
this.inverseWorldToScreenQ = new MathUtil.Quaternion();
this.worldToScreenQ = new MathUtil.Quaternion();
this.originalPoseAdjustQ = new MathUtil.Quaternion();
this.originalPoseAdjustQ.setFromAxisAngle(new MathUtil.Vector3(0, 0, 1),
-window.orientation * Math.PI / 180);
this.setScreenTransform_();
// Adjust this filter for being in landscape mode.
if (Util.isLandscapeMode()) {
this.filterToWorldQ.multiply(this.inverseWorldToScreenQ);
}
// Keep track of a reset transform for resetSensor.
this.resetQ = new MathUtil.Quaternion();
this.isFirefoxAndroid = Util.isFirefoxAndroid();
this.isIOS = Util.isIOS();
this.orientationOut_ = new Float32Array(4);
}
FusionPoseSensor.prototype.getPosition = function() {
// This PoseSensor doesn't support position
return null;
};
FusionPoseSensor.prototype.getOrientation = function() {
// Convert from filter space to the the same system used by the
// deviceorientation event.
var orientation = this.filter.getOrientation();
// Predict orientation.
this.predictedQ = this.posePredictor.getPrediction(orientation, this.gyroscope, this.previousTimestampS);
// Convert to THREE coordinate system: -Z forward, Y up, X right.
var out = new MathUtil.Quaternion();
out.copy(this.filterToWorldQ);
out.multiply(this.resetQ);
if (!WebVRConfig.TOUCH_PANNER_DISABLED) {
out.multiply(this.touchPanner.getOrientation());
}
out.multiply(this.predictedQ);
out.multiply(this.worldToScreenQ);
// Handle the yaw-only case.
if (WebVRConfig.YAW_ONLY) {
// Make a quaternion that only turns around the Y-axis.
out.x = 0;
out.z = 0;
out.normalize();
}
this.orientationOut_[0] = out.x;
this.orientationOut_[1] = out.y;
this.orientationOut_[2] = out.z;
this.orientationOut_[3] = out.w;
return this.orientationOut_;
};
FusionPoseSensor.prototype.resetPose = function() {
// Reduce to inverted yaw-only.
this.resetQ.copy(this.filter.getOrientation());
this.resetQ.x = 0;
this.resetQ.y = 0;
this.resetQ.z *= -1;
this.resetQ.normalize();
// Take into account extra transformations in landscape mode.
if (Util.isLandscapeMode()) {
this.resetQ.multiply(this.inverseWorldToScreenQ);
}
// Take into account original pose.
this.resetQ.multiply(this.originalPoseAdjustQ);
if (!WebVRConfig.TOUCH_PANNER_DISABLED) {
this.touchPanner.resetSensor();
}
};
FusionPoseSensor.prototype.onDeviceMotion_ = function(deviceMotion) {
this.updateDeviceMotion_(deviceMotion);
};
FusionPoseSensor.prototype.updateDeviceMotion_ = function(deviceMotion) {
var accGravity = deviceMotion.accelerationIncludingGravity;
var rotRate = deviceMotion.rotationRate;
var timestampS = deviceMotion.timeStamp / 1000;
// Firefox Android timeStamp returns one thousandth of a millisecond.
if (this.isFirefoxAndroid) {
timestampS /= 1000;
}
var deltaS = timestampS - this.previousTimestampS;
if (deltaS <= Util.MIN_TIMESTEP || deltaS > Util.MAX_TIMESTEP) {
console.warn('Invalid timestamps detected. Time step between successive ' +
'gyroscope sensor samples is very small or not monotonic');
this.previousTimestampS = timestampS;
return;
}
this.accelerometer.set(-accGravity.x, -accGravity.y, -accGravity.z);
this.gyroscope.set(rotRate.alpha, rotRate.beta, rotRate.gamma);
// With iOS and Firefox Android, rotationRate is reported in degrees,
// so we first convert to radians.
if (this.isIOS || this.isFirefoxAndroid) {
this.gyroscope.multiplyScalar(Math.PI / 180);
}
this.filter.addAccelMeasurement(this.accelerometer, timestampS);
this.filter.addGyroMeasurement(this.gyroscope, timestampS);
this.previousTimestampS = timestampS;
};
FusionPoseSensor.prototype.onOrientationChange_ = function(screenOrientation) {
this.setScreenTransform_();
};
/**
* This is only needed if we are in an cross origin iframe on iOS to work around
* this issue: https://bugs.webkit.org/show_bug.cgi?id=152299.
*/
FusionPoseSensor.prototype.onMessage_ = function(event) {
var message = event.data;
// If there's no message type, ignore it.
if (!message || !message.type) {
return;
}
// Ignore all messages that aren't devicemotion.
var type = message.type.toLowerCase();
if (type !== 'devicemotion') {
return;
}
// Update device motion.
this.updateDeviceMotion_(message.deviceMotionEvent);
};
FusionPoseSensor.prototype.setScreenTransform_ = function() {
this.worldToScreenQ.set(0, 0, 0, 1);
switch (window.orientation) {
case 0:
break;
case 90:
this.worldToScreenQ.setFromAxisAngle(new MathUtil.Vector3(0, 0, 1), -Math.PI / 2);
break;
case -90:
this.worldToScreenQ.setFromAxisAngle(new MathUtil.Vector3(0, 0, 1), Math.PI / 2);
break;
case 180:
// TODO.
break;
}
this.inverseWorldToScreenQ.copy(this.worldToScreenQ);
this.inverseWorldToScreenQ.inverse();
};
FusionPoseSensor.prototype.start = function() {
this.onDeviceMotionCallback_ = this.onDeviceMotion_.bind(this);
this.onOrientationChangeCallback_ = this.onOrientationChange_.bind(this);
this.onMessageCallback_ = this.onMessage_.bind(this);
// Only listen for postMessages if we're in an iOS and embedded inside a cross
// domain IFrame. In this case, the polyfill can still work if the containing
// page sends synthetic devicemotion events. For an example of this, see
// iframe-message-sender.js in VR View: https://goo.gl/XDtvFZ
if (Util.isIOS() && Util.isInsideCrossDomainIFrame()) {
window.addEventListener('message', this.onMessageCallback_);
}
window.addEventListener('orientationchange', this.onOrientationChangeCallback_);
window.addEventListener('devicemotion', this.onDeviceMotionCallback_);
};
FusionPoseSensor.prototype.stop = function() {
window.removeEventListener('devicemotion', this.onDeviceMotionCallback_);
window.removeEventListener('orientationchange', this.onOrientationChangeCallback_);
window.removeEventListener('message', this.onMessageCallback_);
};
module.exports = FusionPoseSensor;

View File

@@ -0,0 +1,81 @@
/*
* 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 MathUtil = require('../math-util');
var Util = require('../util');
/**
* Given an orientation and the gyroscope data, predicts the future orientation
* of the head. This makes rendering appear faster.
*
* Also see: http://msl.cs.uiuc.edu/~lavalle/papers/LavYerKatAnt14.pdf
*
* @param {Number} predictionTimeS time from head movement to the appearance of
* the corresponding image.
*/
function PosePredictor(predictionTimeS) {
this.predictionTimeS = predictionTimeS;
// The quaternion corresponding to the previous state.
this.previousQ = new MathUtil.Quaternion();
// Previous time a prediction occurred.
this.previousTimestampS = null;
// The delta quaternion that adjusts the current pose.
this.deltaQ = new MathUtil.Quaternion();
// The output quaternion.
this.outQ = new MathUtil.Quaternion();
}
PosePredictor.prototype.getPrediction = function(currentQ, gyro, timestampS) {
if (!this.previousTimestampS) {
this.previousQ.copy(currentQ);
this.previousTimestampS = timestampS;
return currentQ;
}
// Calculate axis and angle based on gyroscope rotation rate data.
var axis = new MathUtil.Vector3();
axis.copy(gyro);
axis.normalize();
var angularSpeed = gyro.length();
// If we're rotating slowly, don't do prediction.
if (angularSpeed < MathUtil.degToRad * 20) {
if (Util.isDebug()) {
console.log('Moving slowly, at %s deg/s: no prediction',
(MathUtil.radToDeg * angularSpeed).toFixed(1));
}
this.outQ.copy(currentQ);
this.previousQ.copy(currentQ);
return this.outQ;
}
// Get the predicted angle based on the time delta and latency.
var deltaT = timestampS - this.previousTimestampS;
var predictAngle = angularSpeed * this.predictionTimeS;
this.deltaQ.setFromAxisAngle(axis, predictAngle);
this.outQ.copy(this.previousQ);
this.outQ.multiply(this.deltaQ);
this.previousQ.copy(currentQ);
this.previousTimestampS = timestampS;
return this.outQ;
};
module.exports = PosePredictor;

View File

@@ -0,0 +1,14 @@
function SensorSample(sample, timestampS) {
this.set(sample, timestampS);
};
SensorSample.prototype.set = function(sample, timestampS) {
this.sample = sample;
this.timestampS = timestampS;
};
SensorSample.prototype.copy = function(sensorSample) {
this.set(sensorSample.sample, sensorSample.timestampS);
};
module.exports = SensorSample;

76
node_modules/webvr-polyfill/src/touch-panner.js generated vendored Normal file
View File

@@ -0,0 +1,76 @@
/*
* 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 MathUtil = require('./math-util.js');
var Util = require('./util.js');
var ROTATE_SPEED = 0.5;
/**
* Provides a quaternion responsible for pre-panning the scene before further
* transformations due to device sensors.
*/
function TouchPanner() {
window.addEventListener('touchstart', this.onTouchStart_.bind(this));
window.addEventListener('touchmove', this.onTouchMove_.bind(this));
window.addEventListener('touchend', this.onTouchEnd_.bind(this));
this.isTouching = false;
this.rotateStart = new MathUtil.Vector2();
this.rotateEnd = new MathUtil.Vector2();
this.rotateDelta = new MathUtil.Vector2();
this.theta = 0;
this.orientation = new MathUtil.Quaternion();
}
TouchPanner.prototype.getOrientation = function() {
this.orientation.setFromEulerXYZ(0, 0, this.theta);
return this.orientation;
};
TouchPanner.prototype.resetSensor = function() {
this.theta = 0;
};
TouchPanner.prototype.onTouchStart_ = function(e) {
// Only respond if there is exactly one touch.
if (e.touches.length != 1) {
return;
}
this.rotateStart.set(e.touches[0].pageX, e.touches[0].pageY);
this.isTouching = true;
};
TouchPanner.prototype.onTouchMove_ = function(e) {
if (!this.isTouching) {
return;
}
this.rotateEnd.set(e.touches[0].pageX, e.touches[0].pageY);
this.rotateDelta.subVectors(this.rotateEnd, this.rotateStart);
this.rotateStart.copy(this.rotateEnd);
// On iOS, direction is inverted.
if (Util.isIOS()) {
this.rotateDelta.x *= -1;
}
var element = document.body;
this.theta += 2 * Math.PI * this.rotateDelta.x / element.clientWidth * ROTATE_SPEED;
};
TouchPanner.prototype.onTouchEnd_ = function(e) {
this.isTouching = false;
};
module.exports = TouchPanner;

430
node_modules/webvr-polyfill/src/util.js generated vendored Normal file
View File

@@ -0,0 +1,430 @@
/*
* 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 objectAssign = require('object-assign');
var Util = window.Util || {};
Util.MIN_TIMESTEP = 0.001;
Util.MAX_TIMESTEP = 1;
Util.base64 = function(mimeType, base64) {
return 'data:' + mimeType + ';base64,' + base64;
};
Util.clamp = function(value, min, max) {
return Math.min(Math.max(min, value), max);
};
Util.lerp = function(a, b, t) {
return a + ((b - a) * t);
};
Util.isIOS = (function() {
var isIOS = /iPad|iPhone|iPod/.test(navigator.platform);
return function() {
return isIOS;
};
})();
Util.isSafari = (function() {
var isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
return function() {
return isSafari;
};
})();
Util.isFirefoxAndroid = (function() {
var isFirefoxAndroid = navigator.userAgent.indexOf('Firefox') !== -1 &&
navigator.userAgent.indexOf('Android') !== -1;
return function() {
return isFirefoxAndroid;
};
})();
Util.isLandscapeMode = function() {
return (window.orientation == 90 || window.orientation == -90);
};
// Helper method to validate the time steps of sensor timestamps.
Util.isTimestampDeltaValid = function(timestampDeltaS) {
if (isNaN(timestampDeltaS)) {
return false;
}
if (timestampDeltaS <= Util.MIN_TIMESTEP) {
return false;
}
if (timestampDeltaS > Util.MAX_TIMESTEP) {
return false;
}
return true;
};
Util.getScreenWidth = function() {
return Math.max(window.screen.width, window.screen.height) *
window.devicePixelRatio;
};
Util.getScreenHeight = function() {
return Math.min(window.screen.width, window.screen.height) *
window.devicePixelRatio;
};
Util.requestFullscreen = function(element) {
if (element.requestFullscreen) {
element.requestFullscreen();
} else if (element.webkitRequestFullscreen) {
element.webkitRequestFullscreen();
} else if (element.mozRequestFullScreen) {
element.mozRequestFullScreen();
} else if (element.msRequestFullscreen) {
element.msRequestFullscreen();
} else {
return false;
}
return true;
};
Util.exitFullscreen = function() {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen();
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else if (document.msExitFullscreen) {
document.msExitFullscreen();
} else {
return false;
}
return true;
};
Util.getFullscreenElement = function() {
return document.fullscreenElement ||
document.webkitFullscreenElement ||
document.mozFullScreenElement ||
document.msFullscreenElement;
};
Util.linkProgram = function(gl, vertexSource, fragmentSource, attribLocationMap) {
// No error checking for brevity.
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexSource);
gl.compileShader(vertexShader);
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentSource);
gl.compileShader(fragmentShader);
var program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
for (var attribName in attribLocationMap)
gl.bindAttribLocation(program, attribLocationMap[attribName], attribName);
gl.linkProgram(program);
gl.deleteShader(vertexShader);
gl.deleteShader(fragmentShader);
return program;
};
Util.getProgramUniforms = function(gl, program) {
var uniforms = {};
var uniformCount = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
var uniformName = '';
for (var i = 0; i < uniformCount; i++) {
var uniformInfo = gl.getActiveUniform(program, i);
uniformName = uniformInfo.name.replace('[0]', '');
uniforms[uniformName] = gl.getUniformLocation(program, uniformName);
}
return uniforms;
};
Util.orthoMatrix = function (out, left, right, bottom, top, near, far) {
var lr = 1 / (left - right),
bt = 1 / (bottom - top),
nf = 1 / (near - far);
out[0] = -2 * lr;
out[1] = 0;
out[2] = 0;
out[3] = 0;
out[4] = 0;
out[5] = -2 * bt;
out[6] = 0;
out[7] = 0;
out[8] = 0;
out[9] = 0;
out[10] = 2 * nf;
out[11] = 0;
out[12] = (left + right) * lr;
out[13] = (top + bottom) * bt;
out[14] = (far + near) * nf;
out[15] = 1;
return out;
};
Util.isMobile = function() {
var check = false;
(function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))check = true})(navigator.userAgent||navigator.vendor||window.opera);
return check;
};
Util.extend = objectAssign;
Util.safariCssSizeWorkaround = function(canvas) {
// TODO(smus): Remove this workaround when Safari for iOS is fixed.
// iOS only workaround (for https://bugs.webkit.org/show_bug.cgi?id=152556).
//
// "To the last I grapple with thee;
// from hell's heart I stab at thee;
// for hate's sake I spit my last breath at thee."
// -- Moby Dick, by Herman Melville
if (Util.isIOS()) {
var width = canvas.style.width;
var height = canvas.style.height;
canvas.style.width = (parseInt(width) + 1) + 'px';
canvas.style.height = (parseInt(height)) + 'px';
setTimeout(function() {
canvas.style.width = width;
canvas.style.height = height;
}, 100);
}
// Debug only.
window.Util = Util;
window.canvas = canvas;
};
Util.isDebug = function() {
return Util.getQueryParameter('debug');
};
Util.getQueryParameter = function(name) {
var name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
results = regex.exec(location.search);
return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
};
Util.frameDataFromPose = (function() {
var piOver180 = Math.PI / 180.0;
var rad45 = Math.PI * 0.25;
// Borrowed from glMatrix.
function mat4_perspectiveFromFieldOfView(out, fov, near, far) {
var upTan = Math.tan(fov ? (fov.upDegrees * piOver180) : rad45),
downTan = Math.tan(fov ? (fov.downDegrees * piOver180) : rad45),
leftTan = Math.tan(fov ? (fov.leftDegrees * piOver180) : rad45),
rightTan = Math.tan(fov ? (fov.rightDegrees * piOver180) : rad45),
xScale = 2.0 / (leftTan + rightTan),
yScale = 2.0 / (upTan + downTan);
out[0] = xScale;
out[1] = 0.0;
out[2] = 0.0;
out[3] = 0.0;
out[4] = 0.0;
out[5] = yScale;
out[6] = 0.0;
out[7] = 0.0;
out[8] = -((leftTan - rightTan) * xScale * 0.5);
out[9] = ((upTan - downTan) * yScale * 0.5);
out[10] = far / (near - far);
out[11] = -1.0;
out[12] = 0.0;
out[13] = 0.0;
out[14] = (far * near) / (near - far);
out[15] = 0.0;
return out;
}
function mat4_fromRotationTranslation(out, q, v) {
// Quaternion math
var x = q[0], y = q[1], z = q[2], w = q[3],
x2 = x + x,
y2 = y + y,
z2 = z + z,
xx = x * x2,
xy = x * y2,
xz = x * z2,
yy = y * y2,
yz = y * z2,
zz = z * z2,
wx = w * x2,
wy = w * y2,
wz = w * z2;
out[0] = 1 - (yy + zz);
out[1] = xy + wz;
out[2] = xz - wy;
out[3] = 0;
out[4] = xy - wz;
out[5] = 1 - (xx + zz);
out[6] = yz + wx;
out[7] = 0;
out[8] = xz + wy;
out[9] = yz - wx;
out[10] = 1 - (xx + yy);
out[11] = 0;
out[12] = v[0];
out[13] = v[1];
out[14] = v[2];
out[15] = 1;
return out;
};
function mat4_translate(out, a, v) {
var x = v[0], y = v[1], z = v[2],
a00, a01, a02, a03,
a10, a11, a12, a13,
a20, a21, a22, a23;
if (a === out) {
out[12] = a[0] * x + a[4] * y + a[8] * z + a[12];
out[13] = a[1] * x + a[5] * y + a[9] * z + a[13];
out[14] = a[2] * x + a[6] * y + a[10] * z + a[14];
out[15] = a[3] * x + a[7] * y + a[11] * z + a[15];
} else {
a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3];
a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7];
a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11];
out[0] = a00; out[1] = a01; out[2] = a02; out[3] = a03;
out[4] = a10; out[5] = a11; out[6] = a12; out[7] = a13;
out[8] = a20; out[9] = a21; out[10] = a22; out[11] = a23;
out[12] = a00 * x + a10 * y + a20 * z + a[12];
out[13] = a01 * x + a11 * y + a21 * z + a[13];
out[14] = a02 * x + a12 * y + a22 * z + a[14];
out[15] = a03 * x + a13 * y + a23 * z + a[15];
}
return out;
};
function mat4_invert(out, a) {
var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3],
a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7],
a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11],
a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15],
b00 = a00 * a11 - a01 * a10,
b01 = a00 * a12 - a02 * a10,
b02 = a00 * a13 - a03 * a10,
b03 = a01 * a12 - a02 * a11,
b04 = a01 * a13 - a03 * a11,
b05 = a02 * a13 - a03 * a12,
b06 = a20 * a31 - a21 * a30,
b07 = a20 * a32 - a22 * a30,
b08 = a20 * a33 - a23 * a30,
b09 = a21 * a32 - a22 * a31,
b10 = a21 * a33 - a23 * a31,
b11 = a22 * a33 - a23 * a32,
// Calculate the determinant
det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
if (!det) {
return null;
}
det = 1.0 / det;
out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det;
out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det;
out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det;
out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det;
out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det;
out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det;
out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det;
out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det;
out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det;
out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det;
out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det;
out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det;
out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det;
out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det;
out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det;
out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det;
return out;
};
var defaultOrientation = new Float32Array([0, 0, 0, 1]);
var defaultPosition = new Float32Array([0, 0, 0]);
function updateEyeMatrices(projection, view, pose, parameters, vrDisplay) {
mat4_perspectiveFromFieldOfView(projection, parameters ? parameters.fieldOfView : null, vrDisplay.depthNear, vrDisplay.depthFar);
var orientation = pose.orientation || defaultOrientation;
var position = pose.position || defaultPosition;
mat4_fromRotationTranslation(view, orientation, position);
if (parameters)
mat4_translate(view, view, parameters.offset);
mat4_invert(view, view);
}
return function(frameData, pose, vrDisplay) {
if (!frameData || !pose)
return false;
frameData.pose = pose;
frameData.timestamp = pose.timestamp;
updateEyeMatrices(
frameData.leftProjectionMatrix, frameData.leftViewMatrix,
pose, vrDisplay.getEyeParameters("left"), vrDisplay);
updateEyeMatrices(
frameData.rightProjectionMatrix, frameData.rightViewMatrix,
pose, vrDisplay.getEyeParameters("right"), vrDisplay);
return true;
};
})();
Util.isInsideCrossDomainIFrame = function() {
var isFramed = (window.self !== window.top);
var refDomain = Util.getDomainFromUrl(document.referrer);
var thisDomain = Util.getDomainFromUrl(window.location.href);
return isFramed && (refDomain !== thisDomain);
};
// From http://stackoverflow.com/a/23945027.
Util.getDomainFromUrl = function(url) {
var domain;
// Find & remove protocol (http, ftp, etc.) and get domain.
if (url.indexOf("://") > -1) {
domain = url.split('/')[2];
}
else {
domain = url.split('/')[0];
}
//find & remove port number
domain = domain.split(':')[0];
return domain;
}
module.exports = Util;

197
node_modules/webvr-polyfill/src/viewer-selector.js generated vendored Normal file
View File

@@ -0,0 +1,197 @@
/*
* 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 DeviceInfo = require('./device-info.js');
var EventEmitter3 = require('eventemitter3');
var Util = require('./util.js');
var DEFAULT_VIEWER = 'CardboardV1';
var VIEWER_KEY = 'WEBVR_CARDBOARD_VIEWER';
var CLASS_NAME = 'webvr-polyfill-viewer-selector';
/**
* Creates a viewer selector with the options specified. Supports being shown
* and hidden. Generates events when viewer parameters change. Also supports
* saving the currently selected index in localStorage.
*/
function ViewerSelector() {
// Try to load the selected key from local storage. If none exists, use the
// default key.
try {
this.selectedKey = localStorage.getItem(VIEWER_KEY) || DEFAULT_VIEWER;
} catch (error) {
console.error('Failed to load viewer profile: %s', error);
}
this.dialog = this.createDialog_(DeviceInfo.Viewers);
this.root = null;
}
ViewerSelector.prototype = new EventEmitter3();
ViewerSelector.prototype.show = function(root) {
this.root = root;
root.appendChild(this.dialog);
// Ensure the currently selected item is checked.
var selected = this.dialog.querySelector('#' + this.selectedKey);
selected.checked = true;
// Show the UI.
this.dialog.style.display = 'block';
};
ViewerSelector.prototype.hide = function() {
if (this.root && this.root.contains(this.dialog)) {
this.root.removeChild(this.dialog);
}
this.dialog.style.display = 'none';
};
ViewerSelector.prototype.getCurrentViewer = function() {
return DeviceInfo.Viewers[this.selectedKey];
};
ViewerSelector.prototype.getSelectedKey_ = function() {
var input = this.dialog.querySelector('input[name=field]:checked');
if (input) {
return input.id;
}
return null;
};
ViewerSelector.prototype.onSave_ = function() {
this.selectedKey = this.getSelectedKey_();
if (!this.selectedKey || !DeviceInfo.Viewers[this.selectedKey]) {
console.error('ViewerSelector.onSave_: this should never happen!');
return;
}
this.emit('change', DeviceInfo.Viewers[this.selectedKey]);
// Attempt to save the viewer profile, but fails in private mode.
try {
localStorage.setItem(VIEWER_KEY, this.selectedKey);
} catch(error) {
console.error('Failed to save viewer profile: %s', error);
}
this.hide();
};
/**
* Creates the dialog.
*/
ViewerSelector.prototype.createDialog_ = function(options) {
var container = document.createElement('div');
container.classList.add(CLASS_NAME);
container.style.display = 'none';
// Create an overlay that dims the background, and which goes away when you
// tap it.
var overlay = document.createElement('div');
var s = overlay.style;
s.position = 'fixed';
s.left = 0;
s.top = 0;
s.width = '100%';
s.height = '100%';
s.background = 'rgba(0, 0, 0, 0.3)';
overlay.addEventListener('click', this.hide.bind(this));
var width = 280;
var dialog = document.createElement('div');
var s = dialog.style;
s.boxSizing = 'border-box';
s.position = 'fixed';
s.top = '24px';
s.left = '50%';
s.marginLeft = (-width/2) + 'px';
s.width = width + 'px';
s.padding = '24px';
s.overflow = 'hidden';
s.background = '#fafafa';
s.fontFamily = "'Roboto', sans-serif";
s.boxShadow = '0px 5px 20px #666';
dialog.appendChild(this.createH1_('Select your viewer'));
for (var id in options) {
dialog.appendChild(this.createChoice_(id, options[id].label));
}
dialog.appendChild(this.createButton_('Save', this.onSave_.bind(this)));
container.appendChild(overlay);
container.appendChild(dialog);
return container;
};
ViewerSelector.prototype.createH1_ = function(name) {
var h1 = document.createElement('h1');
var s = h1.style;
s.color = 'black';
s.fontSize = '20px';
s.fontWeight = 'bold';
s.marginTop = 0;
s.marginBottom = '24px';
h1.innerHTML = name;
return h1;
};
ViewerSelector.prototype.createChoice_ = function(id, name) {
/*
<div class="choice">
<input id="v1" type="radio" name="field" value="v1">
<label for="v1">Cardboard V1</label>
</div>
*/
var div = document.createElement('div');
div.style.marginTop = '8px';
div.style.color = 'black';
var input = document.createElement('input');
input.style.fontSize = '30px';
input.setAttribute('id', id);
input.setAttribute('type', 'radio');
input.setAttribute('value', id);
input.setAttribute('name', 'field');
var label = document.createElement('label');
label.style.marginLeft = '4px';
label.setAttribute('for', id);
label.innerHTML = name;
div.appendChild(input);
div.appendChild(label);
return div;
};
ViewerSelector.prototype.createButton_ = function(label, onclick) {
var button = document.createElement('button');
button.innerHTML = label;
var s = button.style;
s.float = 'right';
s.textTransform = 'uppercase';
s.color = '#1094f7';
s.fontSize = '14px';
s.letterSpacing = 0;
s.border = 0;
s.background = 'none';
s.marginTop = '16px';
button.addEventListener('click', onclick);
return button;
};
module.exports = ViewerSelector;

74
node_modules/webvr-polyfill/src/wakelock.js generated vendored Normal file

File diff suppressed because one or more lines are too long

236
node_modules/webvr-polyfill/src/webvr-polyfill.js generated vendored Normal file
View File

@@ -0,0 +1,236 @@
/*
* 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 CardboardVRDisplay = require('./cardboard-vr-display.js');
var MouseKeyboardVRDisplay = require('./mouse-keyboard-vr-display.js');
// Uncomment to add positional tracking via webcam.
//var WebcamPositionSensorVRDevice = require('./webcam-position-sensor-vr-device.js');
var VRDisplay = require('./base.js').VRDisplay;
var VRFrameData = require('./base.js').VRFrameData;
var HMDVRDevice = require('./base.js').HMDVRDevice;
var PositionSensorVRDevice = require('./base.js').PositionSensorVRDevice;
var VRDisplayHMDDevice = require('./display-wrappers.js').VRDisplayHMDDevice;
var VRDisplayPositionSensorDevice = require('./display-wrappers.js').VRDisplayPositionSensorDevice;
function WebVRPolyfill() {
this.displays = [];
this.devices = []; // For deprecated objects
this.devicesPopulated = false;
this.nativeWebVRAvailable = this.isWebVRAvailable();
this.nativeLegacyWebVRAvailable = this.isDeprecatedWebVRAvailable();
this.nativeGetVRDisplaysFunc = this.nativeWebVRAvailable ?
navigator.getVRDisplays :
null;
if (!this.nativeLegacyWebVRAvailable) {
this.enablePolyfill();
if (WebVRConfig.ENABLE_DEPRECATED_API) {
this.enableDeprecatedPolyfill();
}
}
// Put a shim in place to update the API to 1.1 if needed.
InstallWebVRSpecShim();
}
WebVRPolyfill.prototype.isWebVRAvailable = function() {
return ('getVRDisplays' in navigator);
};
WebVRPolyfill.prototype.isDeprecatedWebVRAvailable = function() {
return ('getVRDevices' in navigator) || ('mozGetVRDevices' in navigator);
};
WebVRPolyfill.prototype.populateDevices = function() {
if (this.devicesPopulated) {
return;
}
// Initialize our virtual VR devices.
var vrDisplay = null;
// Add a Cardboard VRDisplay on compatible mobile devices
if (this.isCardboardCompatible()) {
vrDisplay = new CardboardVRDisplay();
this.displays.push(vrDisplay);
// For backwards compatibility
if (WebVRConfig.ENABLE_DEPRECATED_API) {
this.devices.push(new VRDisplayHMDDevice(vrDisplay));
this.devices.push(new VRDisplayPositionSensorDevice(vrDisplay));
}
}
// Add a Mouse and Keyboard driven VRDisplay for desktops/laptops
if (!this.isMobile() && !WebVRConfig.MOUSE_KEYBOARD_CONTROLS_DISABLED) {
vrDisplay = new MouseKeyboardVRDisplay();
this.displays.push(vrDisplay);
// For backwards compatibility
if (WebVRConfig.ENABLE_DEPRECATED_API) {
this.devices.push(new VRDisplayHMDDevice(vrDisplay));
this.devices.push(new VRDisplayPositionSensorDevice(vrDisplay));
}
}
// Uncomment to add positional tracking via webcam.
//if (!this.isMobile() && WebVRConfig.ENABLE_DEPRECATED_API) {
// positionDevice = new WebcamPositionSensorVRDevice();
// this.devices.push(positionDevice);
//}
this.devicesPopulated = true;
};
WebVRPolyfill.prototype.enablePolyfill = function() {
// Provide navigator.getVRDisplays.
navigator.getVRDisplays = this.getVRDisplays.bind(this);
// Provide the VRDisplay object.
window.VRDisplay = VRDisplay;
// Provide navigator.vrEnabled.
var self = this;
Object.defineProperty(navigator, 'vrEnabled', {
get: function () {
return self.isCardboardCompatible() &&
(self.isFullScreenAvailable() || Util.isIOS());
}
});
if (!'VRFrameData' in window) {
// Provide the VRFrameData object.
window.VRFrameData = VRFrameData;
}
};
WebVRPolyfill.prototype.enableDeprecatedPolyfill = function() {
// Provide navigator.getVRDevices.
navigator.getVRDevices = this.getVRDevices.bind(this);
// Provide the CardboardHMDVRDevice and PositionSensorVRDevice objects.
window.HMDVRDevice = HMDVRDevice;
window.PositionSensorVRDevice = PositionSensorVRDevice;
};
WebVRPolyfill.prototype.getVRDisplays = function() {
this.populateDevices();
var polyfillDisplays = this.displays;
if (this.nativeWebVRAvailable) {
return this.nativeGetVRDisplaysFunc.call(navigator).then(function(nativeDisplays) {
if (WebVRConfig.ALWAYS_APPEND_POLYFILL_DISPLAY) {
return nativeDisplays.concat(polyfillDisplays);
} else {
return nativeDisplays.length > 0 ? nativeDisplays : polyfillDisplays;
}
});
} else {
return new Promise(function(resolve, reject) {
try {
resolve(polyfillDisplays);
} catch (e) {
reject(e);
}
});
}
};
WebVRPolyfill.prototype.getVRDevices = function() {
console.warn('getVRDevices is deprecated. Please update your code to use getVRDisplays instead.');
var self = this;
return new Promise(function(resolve, reject) {
try {
if (!self.devicesPopulated) {
if (self.nativeWebVRAvailable) {
return navigator.getVRDisplays(function(displays) {
for (var i = 0; i < displays.length; ++i) {
self.devices.push(new VRDisplayHMDDevice(displays[i]));
self.devices.push(new VRDisplayPositionSensorDevice(displays[i]));
}
self.devicesPopulated = true;
resolve(self.devices);
}, reject);
}
if (self.nativeLegacyWebVRAvailable) {
return (navigator.getVRDDevices || navigator.mozGetVRDevices)(function(devices) {
for (var i = 0; i < devices.length; ++i) {
if (devices[i] instanceof HMDVRDevice) {
self.devices.push(devices[i]);
}
if (devices[i] instanceof PositionSensorVRDevice) {
self.devices.push(devices[i]);
}
}
self.devicesPopulated = true;
resolve(self.devices);
}, reject);
}
}
self.populateDevices();
resolve(self.devices);
} catch (e) {
reject(e);
}
});
};
/**
* Determine if a device is mobile.
*/
WebVRPolyfill.prototype.isMobile = function() {
return /Android/i.test(navigator.userAgent) ||
/iPhone|iPad|iPod/i.test(navigator.userAgent);
};
WebVRPolyfill.prototype.isCardboardCompatible = function() {
// For now, support all iOS and Android devices.
// Also enable the WebVRConfig.FORCE_VR flag for debugging.
return this.isMobile() || WebVRConfig.FORCE_ENABLE_VR;
};
WebVRPolyfill.prototype.isFullScreenAvailable = function() {
return (document.fullscreenEnabled ||
document.mozFullScreenEnabled ||
document.webkitFullscreenEnabled ||
false);
};
// Installs a shim that updates a WebVR 1.0 spec implementation to WebVR 1.1
function InstallWebVRSpecShim() {
if ('VRDisplay' in window && !('VRFrameData' in window)) {
// Provide the VRFrameData object.
window.VRFrameData = VRFrameData;
// A lot of Chrome builds don't have depthNear and depthFar, even
// though they're in the WebVR 1.0 spec. Patch them in if they're not present.
if(!('depthNear' in window.VRDisplay.prototype)) {
window.VRDisplay.prototype.depthNear = 0.01;
}
if(!('depthFar' in window.VRDisplay.prototype)) {
window.VRDisplay.prototype.depthFar = 10000.0;
}
window.VRDisplay.prototype.getFrameData = function(frameData) {
return Util.frameDataFromPose(frameData, this.getPose(), this);
}
}
};
module.exports.WebVRPolyfill = WebVRPolyfill;