webvr js meetup initial commit
This commit is contained in:
486
build/device-info-test.js
Normal file
486
build/device-info-test.js
Normal file
@@ -0,0 +1,486 @@
|
||||
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
|
||||
/*
|
||||
* 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 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.CardboardV1;
|
||||
this.updateDeviceParams(deviceParams);
|
||||
}
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
DeviceInfo.prototype.determineDevice_ = function(deviceParams) {
|
||||
if (!deviceParams) {
|
||||
// No parameters, so use a default.
|
||||
if (Util.isIOS()) {
|
||||
console.warn("Using fallback Android device measurements.");
|
||||
return DEFAULT_IOS;
|
||||
} else {
|
||||
console.warn("Using fallback iOS 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 = new Distortion(viewer.distortionCoefficients);
|
||||
|
||||
// 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 = THREE.Math.radToDeg(Math.atan(
|
||||
distortion.distort(outerDist / eyeToScreenDistance)));
|
||||
var innerAngle = THREE.Math.radToDeg(Math.atan(
|
||||
distortion.distort(innerDist / eyeToScreenDistance)));
|
||||
var bottomAngle = THREE.Math.radToDeg(Math.atan(
|
||||
distortion.distort(bottomDist / eyeToScreenDistance)));
|
||||
var topAngle = THREE.Math.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)
|
||||
}
|
||||
};
|
||||
|
||||
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 a projection matrix for the left eye.
|
||||
*/
|
||||
DeviceInfo.prototype.getProjectionMatrixLeftEye = function(opt_isUndistorted) {
|
||||
var fov = this.getFieldOfViewLeftEye(opt_isUndistorted);
|
||||
|
||||
var projectionMatrix = new THREE.Matrix4();
|
||||
var near = 0.1;
|
||||
var far = 1000;
|
||||
var left = Math.tan(THREE.Math.degToRad(fov.leftDegrees)) * near;
|
||||
var right = Math.tan(THREE.Math.degToRad(fov.rightDegrees)) * near;
|
||||
var bottom = Math.tan(THREE.Math.degToRad(fov.downDegrees)) * near;
|
||||
var top = Math.tan(THREE.Math.degToRad(fov.upDegrees)) * near;
|
||||
|
||||
// makeFrustum expects units in tan-angle space.
|
||||
projectionMatrix.makeFrustum(-left, right, -bottom, top, near, far);
|
||||
|
||||
return projectionMatrix;
|
||||
};
|
||||
|
||||
|
||||
DeviceInfo.prototype.getUndistortedViewportLeftEye = function() {
|
||||
var p = this.getUndistortedParams_();
|
||||
var viewer = this.viewer;
|
||||
var device = this.device;
|
||||
|
||||
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
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculates undistorted field of view for the left eye.
|
||||
*/
|
||||
DeviceInfo.prototype.getUndistortedFieldOfViewLeftEye = function() {
|
||||
var p = this.getUndistortedParams_();
|
||||
|
||||
return {
|
||||
leftDegrees: THREE.Math.radToDeg(Math.atan(p.outerDist)),
|
||||
rightDegrees: THREE.Math.radToDeg(Math.atan(p.innerDist)),
|
||||
downDegrees: THREE.Math.radToDeg(Math.atan(p.bottomDist)),
|
||||
upDegrees: THREE.Math.radToDeg(Math.atan(p.topDist))
|
||||
};
|
||||
};
|
||||
|
||||
DeviceInfo.prototype.getUndistortedParams_ = function() {
|
||||
var viewer = this.viewer;
|
||||
var device = this.device;
|
||||
var distortion = new Distortion(viewer.distortionCoefficients);
|
||||
|
||||
// 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(THREE.Math.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;
|
||||
|
||||
},{"./distortion/distortion.js":2,"./util.js":3}],2:[function(require,module,exports){
|
||||
/**
|
||||
* 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 = radius / 0.9;
|
||||
var r1 = radius * 0.9;
|
||||
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) {
|
||||
return radius * this.distortionFactor_(radius);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the distortion factor of a point.
|
||||
*
|
||||
* @param {Number} radius Radius of the point from the lens center in tan-angle units.
|
||||
* @return {Number} The distortion factor. Multiply by this factor to distort points.
|
||||
*/
|
||||
Distortion.prototype.distortionFactor_ = function(radius) {
|
||||
var result = 1.0;
|
||||
var rFactor = 1.0;
|
||||
var rSquared = radius * radius;
|
||||
|
||||
for (var i = 0; i < this.coefficients.length; i++) {
|
||||
var ki = this.coefficients[i];
|
||||
rFactor *= rSquared;
|
||||
result += ki * rFactor;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
module.exports = Distortion;
|
||||
|
||||
},{}],3:[function(require,module,exports){
|
||||
/*
|
||||
* 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 = {};
|
||||
|
||||
Util.base64 = function(mimeType, base64) {
|
||||
return 'data:' + mimeType + ';base64,' + base64;
|
||||
};
|
||||
|
||||
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.isFirefox = function() {
|
||||
return /firefox/i.test(navigator.userAgent);
|
||||
};
|
||||
|
||||
Util.isIOS = function() {
|
||||
return /(iPad|iPhone|iPod)/g.test(navigator.userAgent);
|
||||
};
|
||||
|
||||
Util.isIFrame = function() {
|
||||
try {
|
||||
return window.self !== window.top;
|
||||
} catch (e) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
Util.appendQueryParameter = function(url, key, value) {
|
||||
// Determine delimiter based on if the URL already GET parameters in it.
|
||||
var delimiter = (url.indexOf('?') < 0 ? '?' : '&');
|
||||
url += delimiter + key + '=' + value;
|
||||
return url;
|
||||
};
|
||||
|
||||
// From http://goo.gl/4WX3tg
|
||||
Util.getQueryParameter = function(name) {
|
||||
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
|
||||
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
|
||||
results = regex.exec(location.search);
|
||||
return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
|
||||
};
|
||||
|
||||
Util.isLandscapeMode = function() {
|
||||
return (window.orientation == 90 || window.orientation == -90);
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
/**
|
||||
* Utility to convert the projection matrix to a vector accepted by the shader.
|
||||
*
|
||||
* @param {Object} opt_params A rectangle to scale this vector by.
|
||||
*/
|
||||
Util.projectionMatrixToVector_ = function(matrix, opt_params) {
|
||||
var params = opt_params || {};
|
||||
var xScale = params.xScale || 1;
|
||||
var yScale = params.yScale || 1;
|
||||
var xTrans = params.xTrans || 0;
|
||||
var yTrans = params.yTrans || 0;
|
||||
|
||||
var elements = matrix.elements;
|
||||
var vec = new THREE.Vector4();
|
||||
vec.set(elements[4*0 + 0] * xScale,
|
||||
elements[4*1 + 1] * yScale,
|
||||
elements[4*2 + 0] - 1 - xTrans,
|
||||
elements[4*2 + 1] - 1 - yTrans).divideScalar(2);
|
||||
return vec;
|
||||
};
|
||||
|
||||
Util.leftProjectionVectorToRight_ = function(left) {
|
||||
//projectionLeft + vec4(0.0, 0.0, 1.0, 0.0)) * vec4(1.0, 1.0, -1.0, 1.0);
|
||||
var out = new THREE.Vector4(0, 0, 1, 0);
|
||||
out.add(left); // out = left + (0, 0, 1, 0).
|
||||
out.z *= -1; // Flip z.
|
||||
|
||||
return out;
|
||||
};
|
||||
|
||||
|
||||
module.exports = Util;
|
||||
|
||||
},{}],4:[function(require,module,exports){
|
||||
var DeviceInfo = require('../src/device-info.js');
|
||||
|
||||
var di = new DeviceInfo();
|
||||
var centroid = di.getLeftEyeCenter();
|
||||
|
||||
// Size the canvas. Render the centroid.
|
||||
var canvas = document.querySelector('canvas');
|
||||
var w = window.innerWidth;
|
||||
var h = window.innerHeight;
|
||||
var x = centroid.x * w/2;
|
||||
var y = centroid.y * h;
|
||||
var size = 10;
|
||||
|
||||
canvas.width = w;
|
||||
canvas.height = h;
|
||||
|
||||
var ctx = canvas.getContext('2d');
|
||||
ctx.clearRect(0, 0, w, h);
|
||||
ctx.fillStyle = 'black';
|
||||
ctx.fillRect(x - size/2, y - size/2, size, size);
|
||||
|
||||
console.log('Placing eye at (%d, %d).', x, y);
|
||||
|
||||
},{"../src/device-info.js":1}]},{},[4]);
|
||||
575
build/webvr-manager.js
Normal file
575
build/webvr-manager.js
Normal file
@@ -0,0 +1,575 @@
|
||||
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.WebVRManager = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
|
||||
/*
|
||||
* 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 Emitter = _dereq_('./emitter.js');
|
||||
var Modes = _dereq_('./modes.js');
|
||||
var Util = _dereq_('./util.js');
|
||||
|
||||
/**
|
||||
* Everything having to do with the WebVR button.
|
||||
* Emits a 'click' event when it's clicked.
|
||||
*/
|
||||
function ButtonManager(opt_root) {
|
||||
var root = opt_root || document.body;
|
||||
this.loadIcons_();
|
||||
|
||||
// Make the fullscreen button.
|
||||
var fsButton = this.createButton();
|
||||
fsButton.src = this.ICONS.fullscreen;
|
||||
fsButton.title = 'Fullscreen mode';
|
||||
var s = fsButton.style;
|
||||
s.bottom = 0;
|
||||
s.right = 0;
|
||||
fsButton.addEventListener('click', this.createClickHandler_('fs'));
|
||||
root.appendChild(fsButton);
|
||||
this.fsButton = fsButton;
|
||||
|
||||
// Make the VR button.
|
||||
var vrButton = this.createButton();
|
||||
vrButton.src = this.ICONS.cardboard;
|
||||
vrButton.title = 'Virtual reality mode';
|
||||
var s = vrButton.style;
|
||||
s.bottom = 0;
|
||||
s.right = '48px';
|
||||
vrButton.addEventListener('click', this.createClickHandler_('vr'));
|
||||
root.appendChild(vrButton);
|
||||
this.vrButton = vrButton;
|
||||
|
||||
this.isVisible = true;
|
||||
|
||||
}
|
||||
ButtonManager.prototype = new Emitter();
|
||||
|
||||
ButtonManager.prototype.createButton = function() {
|
||||
var button = document.createElement('img');
|
||||
button.className = 'webvr-button';
|
||||
var s = button.style;
|
||||
s.position = 'absolute';
|
||||
s.width = '24px'
|
||||
s.height = '24px';
|
||||
s.backgroundSize = 'cover';
|
||||
s.backgroundColor = 'transparent';
|
||||
s.border = 0;
|
||||
s.userSelect = 'none';
|
||||
s.webkitUserSelect = 'none';
|
||||
s.MozUserSelect = 'none';
|
||||
s.cursor = 'pointer';
|
||||
s.padding = '12px';
|
||||
s.zIndex = 1;
|
||||
s.display = 'none';
|
||||
s.boxSizing = 'content-box';
|
||||
|
||||
// Prevent button from being selected and dragged.
|
||||
button.draggable = false;
|
||||
button.addEventListener('dragstart', function(e) {
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
// Style it on hover.
|
||||
button.addEventListener('mouseenter', function(e) {
|
||||
s.filter = s.webkitFilter = 'drop-shadow(0 0 5px rgba(255,255,255,1))';
|
||||
});
|
||||
button.addEventListener('mouseleave', function(e) {
|
||||
s.filter = s.webkitFilter = '';
|
||||
});
|
||||
return button;
|
||||
};
|
||||
|
||||
ButtonManager.prototype.setMode = function(mode, isVRCompatible) {
|
||||
isVRCompatible = isVRCompatible || WebVRConfig.FORCE_ENABLE_VR;
|
||||
if (!this.isVisible) {
|
||||
return;
|
||||
}
|
||||
switch (mode) {
|
||||
case Modes.NORMAL:
|
||||
this.fsButton.style.display = 'block';
|
||||
this.fsButton.src = this.ICONS.fullscreen;
|
||||
this.vrButton.style.display = (isVRCompatible ? 'block' : 'none');
|
||||
break;
|
||||
case Modes.MAGIC_WINDOW:
|
||||
this.fsButton.style.display = 'block';
|
||||
this.fsButton.src = this.ICONS.exitFullscreen;
|
||||
this.vrButton.style.display = 'none';
|
||||
break;
|
||||
case Modes.VR:
|
||||
this.fsButton.style.display = 'none';
|
||||
this.vrButton.style.display = 'none';
|
||||
break;
|
||||
}
|
||||
|
||||
// Hack for Safari Mac/iOS to force relayout (svg-specific issue)
|
||||
// http://goo.gl/hjgR6r
|
||||
var oldValue = this.fsButton.style.display;
|
||||
this.fsButton.style.display = 'inline-block';
|
||||
this.fsButton.offsetHeight;
|
||||
this.fsButton.style.display = oldValue;
|
||||
};
|
||||
|
||||
ButtonManager.prototype.setVisibility = function(isVisible) {
|
||||
this.isVisible = isVisible;
|
||||
this.fsButton.style.display = isVisible ? 'block' : 'none';
|
||||
this.vrButton.style.display = isVisible ? 'block' : 'none';
|
||||
};
|
||||
|
||||
ButtonManager.prototype.createClickHandler_ = function(eventName) {
|
||||
return function(e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
this.emit(eventName);
|
||||
}.bind(this);
|
||||
};
|
||||
|
||||
ButtonManager.prototype.loadIcons_ = function() {
|
||||
// Preload some hard-coded SVG.
|
||||
this.ICONS = {};
|
||||
this.ICONS.cardboard = Util.base64('image/svg+xml', 'PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNHB4IiBoZWlnaHQ9IjI0cHgiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iI0ZGRkZGRiI+CiAgICA8cGF0aCBkPSJNMjAuNzQgNkgzLjIxQzIuNTUgNiAyIDYuNTcgMiA3LjI4djEwLjQ0YzAgLjcuNTUgMS4yOCAxLjIzIDEuMjhoNC43OWMuNTIgMCAuOTYtLjMzIDEuMTQtLjc5bDEuNC0zLjQ4Yy4yMy0uNTkuNzktMS4wMSAxLjQ0LTEuMDFzMS4yMS40MiAxLjQ1IDEuMDFsMS4zOSAzLjQ4Yy4xOS40Ni42My43OSAxLjExLjc5aDQuNzljLjcxIDAgMS4yNi0uNTcgMS4yNi0xLjI4VjcuMjhjMC0uNy0uNTUtMS4yOC0xLjI2LTEuMjh6TTcuNSAxNC42MmMtMS4xNyAwLTIuMTMtLjk1LTIuMTMtMi4xMiAwLTEuMTcuOTYtMi4xMyAyLjEzLTIuMTMgMS4xOCAwIDIuMTIuOTYgMi4xMiAyLjEzcy0uOTUgMi4xMi0yLjEyIDIuMTJ6bTkgMGMtMS4xNyAwLTIuMTMtLjk1LTIuMTMtMi4xMiAwLTEuMTcuOTYtMi4xMyAyLjEzLTIuMTNzMi4xMi45NiAyLjEyIDIuMTMtLjk1IDIuMTItMi4xMiAyLjEyeiIvPgogICAgPHBhdGggZmlsbD0ibm9uZSIgZD0iTTAgMGgyNHYyNEgwVjB6Ii8+Cjwvc3ZnPgo=');
|
||||
this.ICONS.fullscreen = Util.base64('image/svg+xml', 'PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNHB4IiBoZWlnaHQ9IjI0cHgiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iI0ZGRkZGRiI+CiAgICA8cGF0aCBkPSJNMCAwaDI0djI0SDB6IiBmaWxsPSJub25lIi8+CiAgICA8cGF0aCBkPSJNNyAxNEg1djVoNXYtMkg3di0zem0tMi00aDJWN2gzVjVINXY1em0xMiA3aC0zdjJoNXYtNWgtMnYzek0xNCA1djJoM3YzaDJWNWgtNXoiLz4KPC9zdmc+Cg==');
|
||||
this.ICONS.exitFullscreen = Util.base64('image/svg+xml', 'PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNHB4IiBoZWlnaHQ9IjI0cHgiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iI0ZGRkZGRiI+CiAgICA8cGF0aCBkPSJNMCAwaDI0djI0SDB6IiBmaWxsPSJub25lIi8+CiAgICA8cGF0aCBkPSJNNSAxNmgzdjNoMnYtNUg1djJ6bTMtOEg1djJoNVY1SDh2M3ptNiAxMWgydi0zaDN2LTJoLTV2NXptMi0xMVY1aC0ydjVoNVY4aC0zeiIvPgo8L3N2Zz4K');
|
||||
this.ICONS.settings = Util.base64('image/svg+xml', 'PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNHB4IiBoZWlnaHQ9IjI0cHgiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iI0ZGRkZGRiI+CiAgICA8cGF0aCBkPSJNMCAwaDI0djI0SDB6IiBmaWxsPSJub25lIi8+CiAgICA8cGF0aCBkPSJNMTkuNDMgMTIuOThjLjA0LS4zMi4wNy0uNjQuMDctLjk4cy0uMDMtLjY2LS4wNy0uOThsMi4xMS0xLjY1Yy4xOS0uMTUuMjQtLjQyLjEyLS42NGwtMi0zLjQ2Yy0uMTItLjIyLS4zOS0uMy0uNjEtLjIybC0yLjQ5IDFjLS41Mi0uNC0xLjA4LS43My0xLjY5LS45OGwtLjM4LTIuNjVDMTQuNDYgMi4xOCAxNC4yNSAyIDE0IDJoLTRjLS4yNSAwLS40Ni4xOC0uNDkuNDJsLS4zOCAyLjY1Yy0uNjEuMjUtMS4xNy41OS0xLjY5Ljk4bC0yLjQ5LTFjLS4yMy0uMDktLjQ5IDAtLjYxLjIybC0yIDMuNDZjLS4xMy4yMi0uMDcuNDkuMTIuNjRsMi4xMSAxLjY1Yy0uMDQuMzItLjA3LjY1LS4wNy45OHMuMDMuNjYuMDcuOThsLTIuMTEgMS42NWMtLjE5LjE1LS4yNC40Mi0uMTIuNjRsMiAzLjQ2Yy4xMi4yMi4zOS4zLjYxLjIybDIuNDktMWMuNTIuNCAxLjA4LjczIDEuNjkuOThsLjM4IDIuNjVjLjAzLjI0LjI0LjQyLjQ5LjQyaDRjLjI1IDAgLjQ2LS4xOC40OS0uNDJsLjM4LTIuNjVjLjYxLS4yNSAxLjE3LS41OSAxLjY5LS45OGwyLjQ5IDFjLjIzLjA5LjQ5IDAgLjYxLS4yMmwyLTMuNDZjLjEyLS4yMi4wNy0uNDktLjEyLS42NGwtMi4xMS0xLjY1ek0xMiAxNS41Yy0xLjkzIDAtMy41LTEuNTctMy41LTMuNXMxLjU3LTMuNSAzLjUtMy41IDMuNSAxLjU3IDMuNSAzLjUtMS41NyAzLjUtMy41IDMuNXoiLz4KPC9zdmc+Cg==');
|
||||
};
|
||||
|
||||
module.exports = ButtonManager;
|
||||
|
||||
},{"./emitter.js":2,"./modes.js":3,"./util.js":4}],2:[function(_dereq_,module,exports){
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
function Emitter() {
|
||||
this.callbacks = {};
|
||||
}
|
||||
|
||||
Emitter.prototype.emit = function(eventName) {
|
||||
var callbacks = this.callbacks[eventName];
|
||||
if (!callbacks) {
|
||||
//console.log('No valid callback specified.');
|
||||
return;
|
||||
}
|
||||
var args = [].slice.call(arguments);
|
||||
// Eliminate the first param (the callback).
|
||||
args.shift();
|
||||
for (var i = 0; i < callbacks.length; i++) {
|
||||
callbacks[i].apply(this, args);
|
||||
}
|
||||
};
|
||||
|
||||
Emitter.prototype.on = function(eventName, callback) {
|
||||
if (eventName in this.callbacks) {
|
||||
this.callbacks[eventName].push(callback);
|
||||
} else {
|
||||
this.callbacks[eventName] = [callback];
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Emitter;
|
||||
|
||||
},{}],3:[function(_dereq_,module,exports){
|
||||
/*
|
||||
* 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 Modes = {
|
||||
UNKNOWN: 0,
|
||||
// Not fullscreen, just tracking.
|
||||
NORMAL: 1,
|
||||
// Magic window immersive mode.
|
||||
MAGIC_WINDOW: 2,
|
||||
// Full screen split screen VR mode.
|
||||
VR: 3,
|
||||
};
|
||||
|
||||
module.exports = Modes;
|
||||
|
||||
},{}],4:[function(_dereq_,module,exports){
|
||||
/*
|
||||
* 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 = {};
|
||||
|
||||
Util.base64 = function(mimeType, base64) {
|
||||
return 'data:' + mimeType + ';base64,' + base64;
|
||||
};
|
||||
|
||||
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.isFirefox = function() {
|
||||
return /firefox/i.test(navigator.userAgent);
|
||||
};
|
||||
|
||||
Util.isIOS = function() {
|
||||
return /(iPad|iPhone|iPod)/g.test(navigator.userAgent);
|
||||
};
|
||||
|
||||
Util.isIFrame = function() {
|
||||
try {
|
||||
return window.self !== window.top;
|
||||
} catch (e) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
Util.appendQueryParameter = function(url, key, value) {
|
||||
// Determine delimiter based on if the URL already GET parameters in it.
|
||||
var delimiter = (url.indexOf('?') < 0 ? '?' : '&');
|
||||
url += delimiter + key + '=' + value;
|
||||
return url;
|
||||
};
|
||||
|
||||
// From http://goo.gl/4WX3tg
|
||||
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.isLandscapeMode = function() {
|
||||
return (window.orientation == 90 || window.orientation == -90);
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
module.exports = Util;
|
||||
|
||||
},{}],5:[function(_dereq_,module,exports){
|
||||
/*
|
||||
* 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 ButtonManager = _dereq_('./button-manager.js');
|
||||
var Emitter = _dereq_('./emitter.js');
|
||||
var Modes = _dereq_('./modes.js');
|
||||
var Util = _dereq_('./util.js');
|
||||
|
||||
/**
|
||||
* Helper for getting in and out of VR mode.
|
||||
*/
|
||||
function WebVRManager(renderer, effect, params) {
|
||||
this.params = params || {};
|
||||
|
||||
this.mode = Modes.UNKNOWN;
|
||||
|
||||
// Set option to hide the button.
|
||||
this.hideButton = this.params.hideButton || false;
|
||||
// Whether or not the FOV should be distorted or un-distorted. By default, it
|
||||
// should be distorted, but in the case of vertex shader based distortion,
|
||||
// ensure that we use undistorted parameters.
|
||||
this.predistorted = !!this.params.predistorted;
|
||||
|
||||
// Save the THREE.js renderer and effect for later.
|
||||
this.renderer = renderer;
|
||||
this.effect = effect;
|
||||
var polyfillWrapper = document.querySelector('.webvr-polyfill-fullscreen-wrapper');
|
||||
this.button = new ButtonManager(polyfillWrapper);
|
||||
|
||||
this.isFullscreenDisabled = !!Util.getQueryParameter('no_fullscreen');
|
||||
this.startMode = Modes.NORMAL;
|
||||
var startModeParam = parseInt(Util.getQueryParameter('start_mode'));
|
||||
if (!isNaN(startModeParam)) {
|
||||
this.startMode = startModeParam;
|
||||
}
|
||||
|
||||
if (this.hideButton) {
|
||||
this.button.setVisibility(false);
|
||||
}
|
||||
|
||||
// Check if the browser is compatible with WebVR.
|
||||
this.getDeviceByType_(VRDisplay).then(function(hmd) {
|
||||
this.hmd = hmd;
|
||||
|
||||
// Only enable VR mode if there's a VR device attached or we are running the
|
||||
// polyfill on mobile.
|
||||
if (!this.isVRCompatibleOverride) {
|
||||
this.isVRCompatible = !hmd.isPolyfilled || Util.isMobile();
|
||||
}
|
||||
|
||||
switch (this.startMode) {
|
||||
case Modes.MAGIC_WINDOW:
|
||||
this.setMode_(Modes.MAGIC_WINDOW);
|
||||
break;
|
||||
case Modes.VR:
|
||||
this.enterVRMode_();
|
||||
this.setMode_(Modes.VR);
|
||||
break;
|
||||
default:
|
||||
this.setMode_(Modes.NORMAL);
|
||||
}
|
||||
|
||||
this.emit('initialized');
|
||||
}.bind(this));
|
||||
|
||||
// Hook up button listeners.
|
||||
this.button.on('fs', this.onFSClick_.bind(this));
|
||||
this.button.on('vr', this.onVRClick_.bind(this));
|
||||
|
||||
// Bind to fullscreen events.
|
||||
document.addEventListener('webkitfullscreenchange',
|
||||
this.onFullscreenChange_.bind(this));
|
||||
document.addEventListener('mozfullscreenchange',
|
||||
this.onFullscreenChange_.bind(this));
|
||||
document.addEventListener('msfullscreenchange',
|
||||
this.onFullscreenChange_.bind(this));
|
||||
|
||||
// Bind to VR* specific events.
|
||||
window.addEventListener('vrdisplaypresentchange',
|
||||
this.onVRDisplayPresentChange_.bind(this));
|
||||
window.addEventListener('vrdisplaydeviceparamschange',
|
||||
this.onVRDisplayDeviceParamsChange_.bind(this));
|
||||
}
|
||||
|
||||
WebVRManager.prototype = new Emitter();
|
||||
|
||||
// Expose these values externally.
|
||||
WebVRManager.Modes = Modes;
|
||||
|
||||
WebVRManager.prototype.render = function(scene, camera, timestamp) {
|
||||
// Scene may be an array of two scenes, one for each eye.
|
||||
if (scene instanceof Array) {
|
||||
this.effect.render(scene[0], camera);
|
||||
} else {
|
||||
this.effect.render(scene, camera);
|
||||
}
|
||||
};
|
||||
|
||||
WebVRManager.prototype.setVRCompatibleOverride = function(isVRCompatible) {
|
||||
this.isVRCompatible = isVRCompatible;
|
||||
this.isVRCompatibleOverride = true;
|
||||
|
||||
// Don't actually change modes, just update the buttons.
|
||||
this.button.setMode(this.mode, this.isVRCompatible);
|
||||
};
|
||||
|
||||
WebVRManager.prototype.setFullscreenCallback = function(callback) {
|
||||
this.fullscreenCallback = callback;
|
||||
};
|
||||
|
||||
WebVRManager.prototype.setVRCallback = function(callback) {
|
||||
this.vrCallback = callback;
|
||||
};
|
||||
|
||||
WebVRManager.prototype.setExitFullscreenCallback = function(callback) {
|
||||
this.exitFullscreenCallback = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Promise returns true if there is at least one HMD device available.
|
||||
*/
|
||||
WebVRManager.prototype.getDeviceByType_ = function(type) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
navigator.getVRDisplays().then(function(displays) {
|
||||
// Promise succeeds, but check if there are any displays actually.
|
||||
for (var i = 0; i < displays.length; i++) {
|
||||
if (displays[i] instanceof type) {
|
||||
resolve(displays[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
resolve(null);
|
||||
}, function() {
|
||||
// No displays are found.
|
||||
resolve(null);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper for entering VR mode.
|
||||
*/
|
||||
WebVRManager.prototype.enterVRMode_ = function() {
|
||||
this.hmd.requestPresent([{
|
||||
source: this.renderer.domElement,
|
||||
predistorted: this.predistorted
|
||||
}]);
|
||||
};
|
||||
|
||||
WebVRManager.prototype.setMode_ = function(mode) {
|
||||
var oldMode = this.mode;
|
||||
if (mode == this.mode) {
|
||||
console.warn('Not changing modes, already in %s', mode);
|
||||
return;
|
||||
}
|
||||
// console.log('Mode change: %s => %s', this.mode, mode);
|
||||
this.mode = mode;
|
||||
this.button.setMode(mode, this.isVRCompatible);
|
||||
|
||||
// Emit an event indicating the mode changed.
|
||||
this.emit('modechange', mode, oldMode);
|
||||
};
|
||||
|
||||
/**
|
||||
* Main button was clicked.
|
||||
*/
|
||||
WebVRManager.prototype.onFSClick_ = function() {
|
||||
switch (this.mode) {
|
||||
case Modes.NORMAL:
|
||||
// TODO: Remove this hack if/when iOS gets real fullscreen mode.
|
||||
// If this is an iframe on iOS, break out and open in no_fullscreen mode.
|
||||
if (Util.isIOS() && Util.isIFrame()) {
|
||||
if (this.fullscreenCallback) {
|
||||
this.fullscreenCallback();
|
||||
} else {
|
||||
var url = window.location.href;
|
||||
url = Util.appendQueryParameter(url, 'no_fullscreen', 'true');
|
||||
url = Util.appendQueryParameter(url, 'start_mode', Modes.MAGIC_WINDOW);
|
||||
top.location.href = url;
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.setMode_(Modes.MAGIC_WINDOW);
|
||||
this.requestFullscreen_();
|
||||
break;
|
||||
case Modes.MAGIC_WINDOW:
|
||||
if (this.isFullscreenDisabled) {
|
||||
window.history.back();
|
||||
return;
|
||||
}
|
||||
if (this.exitFullscreenCallback) {
|
||||
this.exitFullscreenCallback();
|
||||
}
|
||||
this.setMode_(Modes.NORMAL);
|
||||
this.exitFullscreen_();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The VR button was clicked.
|
||||
*/
|
||||
WebVRManager.prototype.onVRClick_ = function() {
|
||||
// TODO: Remove this hack when iOS has fullscreen mode.
|
||||
// If this is an iframe on iOS, break out and open in no_fullscreen mode.
|
||||
if (this.mode == Modes.NORMAL && Util.isIOS() && Util.isIFrame()) {
|
||||
if (this.vrCallback) {
|
||||
this.vrCallback();
|
||||
} else {
|
||||
var url = window.location.href;
|
||||
url = Util.appendQueryParameter(url, 'no_fullscreen', 'true');
|
||||
url = Util.appendQueryParameter(url, 'start_mode', Modes.VR);
|
||||
top.location.href = url;
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.enterVRMode_();
|
||||
};
|
||||
|
||||
WebVRManager.prototype.requestFullscreen_ = function() {
|
||||
var canvas = document.body;
|
||||
//var canvas = this.renderer.domElement;
|
||||
if (canvas.requestFullscreen) {
|
||||
canvas.requestFullscreen();
|
||||
} else if (canvas.mozRequestFullScreen) {
|
||||
canvas.mozRequestFullScreen();
|
||||
} else if (canvas.webkitRequestFullscreen) {
|
||||
canvas.webkitRequestFullscreen();
|
||||
} else if (canvas.msRequestFullscreen) {
|
||||
canvas.msRequestFullscreen();
|
||||
}
|
||||
};
|
||||
|
||||
WebVRManager.prototype.exitFullscreen_ = function() {
|
||||
if (document.exitFullscreen) {
|
||||
document.exitFullscreen();
|
||||
} else if (document.mozCancelFullScreen) {
|
||||
document.mozCancelFullScreen();
|
||||
} else if (document.webkitExitFullscreen) {
|
||||
document.webkitExitFullscreen();
|
||||
} else if (document.msExitFullscreen) {
|
||||
document.msExitFullscreen();
|
||||
}
|
||||
};
|
||||
|
||||
WebVRManager.prototype.onVRDisplayPresentChange_ = function(e) {
|
||||
console.log('onVRDisplayPresentChange_', e);
|
||||
if (this.hmd.isPresenting) {
|
||||
this.setMode_(Modes.VR);
|
||||
} else {
|
||||
this.setMode_(Modes.NORMAL);
|
||||
}
|
||||
};
|
||||
|
||||
WebVRManager.prototype.onVRDisplayDeviceParamsChange_ = function(e) {
|
||||
console.log('onVRDisplayDeviceParamsChange_', e);
|
||||
};
|
||||
|
||||
WebVRManager.prototype.onFullscreenChange_ = function(e) {
|
||||
// If we leave full-screen, go back to normal mode.
|
||||
if (document.webkitFullscreenElement === null ||
|
||||
document.mozFullScreenElement === null) {
|
||||
this.setMode_(Modes.NORMAL);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = WebVRManager;
|
||||
|
||||
},{"./button-manager.js":1,"./emitter.js":2,"./modes.js":3,"./util.js":4}]},{},[5])(5)
|
||||
});
|
||||
Reference in New Issue
Block a user