/* Built for whs v2.1.9 */ import { ControlsModule } from 'whs'; import { Matrix4, PerspectiveCamera, REVISION, Vector3 } from 'three'; /** * @author dmarcos / https://github.com/dmarcos * @author mrdoob / http://mrdoob.com * * WebVR Spec: http://mozvr.github.io/webvr-spec/webvr.html * * Firefox: http://mozvr.com/downloads/ * Chromium: https://webvr.info/get-chrome * */ var VREffect = function VREffect(renderer, onError) { var vrDisplay, vrDisplays; var eyeTranslationL = new Vector3(); var eyeTranslationR = new Vector3(); var renderRectL, renderRectR; var frameData = null; if ('VRFrameData' in window) { frameData = new VRFrameData(); } function gotVRDisplays(displays) { vrDisplays = displays; if (displays.length > 0) { vrDisplay = displays[0]; } else { if (onError) onError('HMD not available'); } } if (navigator.getVRDisplays) { navigator.getVRDisplays().then(gotVRDisplays).catch(function () { console.warn('THREE.VREffect: Unable to get VR Displays'); }); } // this.isPresenting = false; this.scale = 1; var scope = this; var rendererSize = renderer.getSize(); var rendererUpdateStyle = false; var rendererPixelRatio = renderer.getPixelRatio(); this.getVRDisplay = function () { return vrDisplay; }; this.setVRDisplay = function (value) { vrDisplay = value; }; this.getVRDisplays = function () { console.warn('THREE.VREffect: getVRDisplays() is being deprecated.'); return vrDisplays; }; this.setSize = function (width, height, updateStyle) { rendererSize = { width: width, height: height }; rendererUpdateStyle = updateStyle; if (scope.isPresenting) { var eyeParamsL = vrDisplay.getEyeParameters('left'); renderer.setPixelRatio(1); renderer.setSize(eyeParamsL.renderWidth * 2, eyeParamsL.renderHeight, false); } else { renderer.setPixelRatio(rendererPixelRatio); renderer.setSize(width, height, updateStyle); } }; // fullscreen var canvas = renderer.domElement; var requestFullscreen; var exitFullscreen; var fullscreenElement; var defaultLeftBounds = [0.0, 0.0, 0.5, 1.0]; var defaultRightBounds = [0.5, 0.0, 0.5, 1.0]; function onVRDisplayPresentChange() { var wasPresenting = scope.isPresenting; scope.isPresenting = vrDisplay !== undefined && vrDisplay.isPresenting; if (scope.isPresenting) { var eyeParamsL = vrDisplay.getEyeParameters('left'); var eyeWidth = eyeParamsL.renderWidth; var eyeHeight = eyeParamsL.renderHeight; if (!wasPresenting) { rendererPixelRatio = renderer.getPixelRatio(); rendererSize = renderer.getSize(); renderer.setPixelRatio(1); renderer.setSize(eyeWidth * 2, eyeHeight, false); } } else if (wasPresenting) { renderer.setPixelRatio(rendererPixelRatio); renderer.setSize(rendererSize.width, rendererSize.height, rendererUpdateStyle); } } window.addEventListener('vrdisplaypresentchange', onVRDisplayPresentChange, false); this.setFullScreen = function (boolean) { return new Promise(function (resolve, reject) { if (vrDisplay === undefined) { reject(new Error('No VR hardware found.')); return; } if (scope.isPresenting === boolean) { resolve(); return; } if (boolean) { resolve(vrDisplay.requestPresent([{ source: canvas }])); } else { resolve(vrDisplay.exitPresent()); } }); }; this.requestPresent = function () { return this.setFullScreen(true); }; this.exitPresent = function () { return this.setFullScreen(false); }; this.requestAnimationFrame = function (f) { if (vrDisplay !== undefined) { return vrDisplay.requestAnimationFrame(f); } else { return window.requestAnimationFrame(f); } }; this.cancelAnimationFrame = function (h) { if (vrDisplay !== undefined) { vrDisplay.cancelAnimationFrame(h); } else { window.cancelAnimationFrame(h); } }; this.submitFrame = function () { if (vrDisplay !== undefined && scope.isPresenting) { vrDisplay.submitFrame(); } }; this.autoSubmitFrame = true; // render var cameraL = new PerspectiveCamera(); cameraL.layers.enable(1); var cameraR = new PerspectiveCamera(); cameraR.layers.enable(2); this.render = function (scene, camera, renderTarget, forceClear) { if (vrDisplay && scope.isPresenting) { var autoUpdate = scene.autoUpdate; if (autoUpdate) { scene.updateMatrixWorld(); scene.autoUpdate = false; } var eyeParamsL = vrDisplay.getEyeParameters('left'); var eyeParamsR = vrDisplay.getEyeParameters('right'); eyeTranslationL.fromArray(eyeParamsL.offset); eyeTranslationR.fromArray(eyeParamsR.offset); if (Array.isArray(scene)) { console.warn('THREE.VREffect.render() no longer supports arrays. Use object.layers instead.'); scene = scene[0]; } // When rendering we don't care what the recommended size is, only what the actual size // of the backbuffer is. var size = renderer.getSize(); var layers = vrDisplay.getLayers(); var leftBounds; var rightBounds; if (layers.length) { var layer = layers[0]; leftBounds = layer.leftBounds !== null && layer.leftBounds.length === 4 ? layer.leftBounds : defaultLeftBounds; rightBounds = layer.rightBounds !== null && layer.rightBounds.length === 4 ? layer.rightBounds : defaultRightBounds; } else { leftBounds = defaultLeftBounds; rightBounds = defaultRightBounds; } renderRectL = { x: Math.round(size.width * leftBounds[0]), y: Math.round(size.height * leftBounds[1]), width: Math.round(size.width * leftBounds[2]), height: Math.round(size.height * leftBounds[3]) }; renderRectR = { x: Math.round(size.width * rightBounds[0]), y: Math.round(size.height * rightBounds[1]), width: Math.round(size.width * rightBounds[2]), height: Math.round(size.height * rightBounds[3]) }; if (renderTarget) { renderer.setRenderTarget(renderTarget); renderTarget.scissorTest = true; } else { renderer.setRenderTarget(null); renderer.setScissorTest(true); } if (renderer.autoClear || forceClear) renderer.clear(); if (camera.parent === null) camera.updateMatrixWorld(); camera.matrixWorld.decompose(cameraL.position, cameraL.quaternion, cameraL.scale); camera.matrixWorld.decompose(cameraR.position, cameraR.quaternion, cameraR.scale); var scale = this.scale; cameraL.translateOnAxis(eyeTranslationL, scale); cameraR.translateOnAxis(eyeTranslationR, scale); if (vrDisplay.getFrameData) { vrDisplay.depthNear = camera.near; vrDisplay.depthFar = camera.far; vrDisplay.getFrameData(frameData); cameraL.projectionMatrix.elements = frameData.leftProjectionMatrix; cameraR.projectionMatrix.elements = frameData.rightProjectionMatrix; } else { cameraL.projectionMatrix = fovToProjection(eyeParamsL.fieldOfView, true, camera.near, camera.far); cameraR.projectionMatrix = fovToProjection(eyeParamsR.fieldOfView, true, camera.near, camera.far); } // render left eye if (renderTarget) { renderTarget.viewport.set(renderRectL.x, renderRectL.y, renderRectL.width, renderRectL.height); renderTarget.scissor.set(renderRectL.x, renderRectL.y, renderRectL.width, renderRectL.height); } else { renderer.setViewport(renderRectL.x, renderRectL.y, renderRectL.width, renderRectL.height); renderer.setScissor(renderRectL.x, renderRectL.y, renderRectL.width, renderRectL.height); } renderer.render(scene, cameraL, renderTarget, forceClear); // render right eye if (renderTarget) { renderTarget.viewport.set(renderRectR.x, renderRectR.y, renderRectR.width, renderRectR.height); renderTarget.scissor.set(renderRectR.x, renderRectR.y, renderRectR.width, renderRectR.height); } else { renderer.setViewport(renderRectR.x, renderRectR.y, renderRectR.width, renderRectR.height); renderer.setScissor(renderRectR.x, renderRectR.y, renderRectR.width, renderRectR.height); } renderer.render(scene, cameraR, renderTarget, forceClear); if (renderTarget) { renderTarget.viewport.set(0, 0, size.width, size.height); renderTarget.scissor.set(0, 0, size.width, size.height); renderTarget.scissorTest = false; renderer.setRenderTarget(null); } else { renderer.setViewport(0, 0, size.width, size.height); renderer.setScissorTest(false); } if (autoUpdate) { scene.autoUpdate = true; } if (scope.autoSubmitFrame) { scope.submitFrame(); } return; } // Regular render mode if not HMD renderer.render(scene, camera, renderTarget, forceClear); }; this.dispose = function () { window.removeEventListener('vrdisplaypresentchange', onVRDisplayPresentChange, false); }; // function fovToNDCScaleOffset(fov) { var pxscale = 2.0 / (fov.leftTan + fov.rightTan); var pxoffset = (fov.leftTan - fov.rightTan) * pxscale * 0.5; var pyscale = 2.0 / (fov.upTan + fov.downTan); var pyoffset = (fov.upTan - fov.downTan) * pyscale * 0.5; return { scale: [pxscale, pyscale], offset: [pxoffset, pyoffset] }; } function fovPortToProjection(fov, rightHanded, zNear, zFar) { rightHanded = rightHanded === undefined ? true : rightHanded; zNear = zNear === undefined ? 0.01 : zNear; zFar = zFar === undefined ? 10000.0 : zFar; var handednessScale = rightHanded ? -1.0 : 1.0; // start with an identity matrix var mobj = new Matrix4(); var m = mobj.elements; // and with scale/offset info for normalized device coords var scaleAndOffset = fovToNDCScaleOffset(fov); // X result, map clip edges to [-w,+w] m[0 * 4 + 0] = scaleAndOffset.scale[0]; m[0 * 4 + 1] = 0.0; m[0 * 4 + 2] = scaleAndOffset.offset[0] * handednessScale; m[0 * 4 + 3] = 0.0; // Y result, map clip edges to [-w,+w] // Y offset is negated because this proj matrix transforms from world coords with Y=up, // but the NDC scaling has Y=down (thanks D3D?) m[1 * 4 + 0] = 0.0; m[1 * 4 + 1] = scaleAndOffset.scale[1]; m[1 * 4 + 2] = -scaleAndOffset.offset[1] * handednessScale; m[1 * 4 + 3] = 0.0; // Z result (up to the app) m[2 * 4 + 0] = 0.0; m[2 * 4 + 1] = 0.0; m[2 * 4 + 2] = zFar / (zNear - zFar) * -handednessScale; m[2 * 4 + 3] = zFar * zNear / (zNear - zFar); // W result (= Z in) m[3 * 4 + 0] = 0.0; m[3 * 4 + 1] = 0.0; m[3 * 4 + 2] = handednessScale; m[3 * 4 + 3] = 0.0; mobj.transpose(); return mobj; } function fovToProjection(fov, rightHanded, zNear, zFar) { var DEG2RAD = Math.PI / 180.0; var fovPort = { upTan: Math.tan(fov.upDegrees * DEG2RAD), downTan: Math.tan(fov.downDegrees * DEG2RAD), leftTan: Math.tan(fov.leftDegrees * DEG2RAD), rightTan: Math.tan(fov.rightDegrees * DEG2RAD) }; return fovPortToProjection(fovPort, rightHanded, zNear, zFar); } }; /** * @author dmarcos / https://github.com/dmarcos * @author mrdoob / http://mrdoob.com * @author halvves / https://github.com/halvves (i only es6 moduled it) */ class VRControls$1 { constructor(camera, onError) { this.camera = camera; this.vrDisplay; this.vrDisplays; this.standingMatrix = new Matrix4(); this.frameData = null; if ('VRFrameData' in window) { this.frameData = new VRFrameData(); } if (navigator.getVRDisplays) { navigator .getVRDisplays() .then((displays) => { this.vrDisplays = displays; if (displays.length > 0) { this.vrDisplay = displays[0]; } else { if (onError) onError('VR input not available.'); } }) .catch(() => { console.warn('VRControls: Unable to get VR Displays'); }); } // the Rift SDK returns the position in meters // this scale factor allows the user to define how meters // are converted to scene units. this.scale = 1; // If true will use "standing space" coordinate system where y=0 is the // floor and x=0, z=0 is the center of the room. this.standing = false; // Distance from the users eyes to the floor in meters. Used when // standing=true but the VRDisplay doesn't provide stageParameters. this.userHeight = 1.6; } getVRDisplay() { return this.vrDisplay; }; setVRDisplay(value) { this.vrDisplay = value; }; getVRDisplays() { console.warn('VRControls: getVRDisplays() is being deprecated.'); return this.vrDisplays; }; getStandingMatrix() { return this.standingMatrix; }; update() { const camera = this.camera; if (this.vrDisplay) { let pose; if (this.vrDisplay.getFrameData) { this.vrDisplay.getFrameData(this.frameData); pose = this.frameData.pose; } else if (this.vrDisplay.getPose) { pose = this.vrDisplay.getPose(); } if (pose.orientation !== null) { camera.quaternion.fromArray(pose.orientation); } if (pose.position !== null) { camera.position.fromArray(pose.position); } else { camera.position.set(0, 0, 0); } if (this.standing) { if (this.vrDisplay.stageParameters) { camera.updateMatrix(); this.standingMatrix.fromArray(this.vrDisplay.stageParameters.sittingToStandingTransform); camera.applyMatrix(this.standingMatrix); } else { camera.position.setY(camera.position.y + this.userHeight); } } camera.position.multiplyScalar(this.scale); } }; dispose() { this.vrDisplay = null; }; } /** * @author mrdoob / http://mrdoob.com * @author Mugen87 / https://github.com/Mugen87 * * Based on @tojiro's vr-samples-utils.js */ var WEBVR = { isAvailable: function isAvailable() { console.warn('WEBVR: isAvailable() is being deprecated. Use .checkAvailability() instead.'); return navigator.getVRDisplays !== undefined; }, checkAvailability: function checkAvailability() { return new Promise(function (resolve, reject) { if (navigator.getVRDisplays !== undefined) { navigator.getVRDisplays().then(function (displays) { if (displays.length === 0) { reject('WebVR supported, but no VRDisplays found.'); } else { resolve(); } }); } else { reject('Your browser does not support WebVR. See webvr.info for assistance.'); } }); }, getVRDisplay: function getVRDisplay(onDisplay) { if ('getVRDisplays' in navigator) { navigator.getVRDisplays().then(function (displays) { onDisplay(displays[0]); }); } }, getMessage: function getMessage() { console.warn('WEBVR: getMessage() is being deprecated. Use .getMessageContainer( message ) instead.'); var message; if (navigator.getVRDisplays) { navigator.getVRDisplays().then(function (displays) { if (displays.length === 0) message = 'WebVR supported, but no VRDisplays found.'; }); } else { message = 'Your browser does not support WebVR. See webvr.info for assistance.'; } if (message !== undefined) { var container = document.createElement('div'); container.style.position = 'absolute'; container.style.left = '0'; container.style.top = '0'; container.style.right = '0'; container.style.zIndex = '999'; container.align = 'center'; var error = document.createElement('div'); error.style.fontFamily = 'sans-serif'; error.style.fontSize = '16px'; error.style.fontStyle = 'normal'; error.style.lineHeight = '26px'; error.style.backgroundColor = '#fff'; error.style.color = '#000'; error.style.padding = '10px 20px'; error.style.margin = '50px'; error.style.display = 'inline-block'; error.innerHTML = message; container.appendChild(error); return container; } }, getMessageContainer: function getMessageContainer(message) { var container = document.createElement('div'); container.style.position = 'absolute'; container.style.left = '0'; container.style.top = '0'; container.style.right = '0'; container.style.zIndex = '999'; container.align = 'center'; var error = document.createElement('div'); error.style.fontFamily = 'sans-serif'; error.style.fontSize = '16px'; error.style.fontStyle = 'normal'; error.style.lineHeight = '26px'; error.style.backgroundColor = '#fff'; error.style.color = '#000'; error.style.padding = '10px 20px'; error.style.margin = '50px'; error.style.display = 'inline-block'; error.innerHTML = message; container.appendChild(error); return container; }, getButton: function getButton(display, canvas) { if ('VREffect' in THREE && display instanceof THREE.VREffect) { console.error('WebVR.getButton() now expects a VRDisplay.'); return document.createElement('button'); } var button = document.createElement('button'); button.style.position = 'absolute'; button.style.left = 'calc(50% - 50px)'; button.style.bottom = '20px'; button.style.width = '100px'; button.style.border = '0'; button.style.padding = '8px'; button.style.cursor = 'pointer'; button.style.backgroundColor = '#000'; button.style.color = '#fff'; button.style.fontFamily = 'sans-serif'; button.style.fontSize = '13px'; button.style.fontStyle = 'normal'; button.style.textAlign = 'center'; button.style.zIndex = '999'; if (display) { button.textContent = 'ENTER VR'; button.onclick = function () { display.isPresenting ? display.exitPresent() : display.requestPresent([{ source: canvas }]); }; window.addEventListener('vrdisplaypresentchange', function () { button.textContent = display.isPresenting ? 'EXIT VR' : 'ENTER VR'; }, false); } else { button.textContent = 'NO VR DISPLAY'; } return button; } }; var classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; var createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var inherits = function (subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }; var possibleConstructorReturn = function (self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }; var VRModule = function () { function VRModule() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; classCallCheck(this, VRModule); this.params = Object.assign(params, { message: true, button: true }); this.scene = null; this.camera = null; this.effect = null; } createClass(VRModule, [{ key: 'manager', value: function manager(_manager) { var _this = this; _manager.define('vr'); if (REVISION > 86) console.warn('Please use VRModule2 for Three.js ^0.87.0 (r87)'); var rendering = _manager.use('rendering'); var renderer = _manager.get('renderer'); var resize = _manager.use('resize'); this.effect = new VREffect(renderer); this.scene = _manager.get('scene'); this.camera = _manager.get('camera'); rendering.effect(this.effect); // TODO: Fix resize. resize.addCallback(function (width, height) { _this.effect.setSize(+width, +height); }); // WEBVR var _params = this.params, message = _params.message, button = _params.button; if (message) WEBVR.checkAvailability().catch(function (message) { document.body.appendChild(WEBVR.getMessageContainer(message)); }); if (button) WEBVR.getVRDisplay(function (display) { var vrbtn = WEBVR.getButton(display, renderer.domElement); vrbtn.className = 'vr-btn'; document.body.appendChild(vrbtn); }); } }]); return VRModule; }(); var VR2Module = function () { function VR2Module() { classCallCheck(this, VR2Module); this.display = new Promise(function (resolve) { return WEBVR.getVRDisplay(function (display) { return resolve(display); }); }); } createClass(VR2Module, [{ key: 'manager', value: function manager(_manager2) { _manager2.define('vr'); var renderer = _manager2.get('renderer'); renderer.vr.enabled = true; console.log(REVISION); console.log(1); this.display.then(function (display) { renderer.vr.setDevice(display); var vrbtn = WEBVR.getButton(display, renderer.domElement); vrbtn.className = 'vr-btn'; document.body.appendChild(vrbtn); }); } }]); return VR2Module; }(); var VRControls = function (_ControlsModule) { inherits(VRControls, _ControlsModule); function VRControls(_ref) { var object = _ref.object, onError = _ref.onError, intensity = _ref.intensity; classCallCheck(this, VRControls); var controls = new VRControls$1(object.native, onError); controls.standing = true; controls.scale = intensity; return possibleConstructorReturn(this, (VRControls.__proto__ || Object.getPrototypeOf(VRControls)).call(this, { controls: controls })); } return VRControls; }(ControlsModule); export { WEBVR, VRModule, VR2Module, VRControls }; //# sourceMappingURL=VRKit.module.js.map