- request an xr device
- if device is available, advertise xr functionality to user
- on user input, request xr session from device
Last active
May 29, 2018 23:03
-
-
Save gabrielflorit/89e567f3893f365774a8a911fa81ee90 to your computer and use it in GitHub Desktop.
WebXR - first test
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
license: mit | |
height: 500 | |
border: no |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Log from './log.js' | |
const log = Log() | |
// XR globals. | |
let xrButton = document.getElementById('xr-button') | |
let xrDevice = null | |
let xrSession = null | |
let xrFrameOfRef = null | |
// WebGL scene globals. | |
let gl = null | |
// Checks to see if WebXR is available and, if so, requests an XRDevice | |
// that is connected to the system and tests it to ensure it supports the | |
// desired session options. | |
function initXR () { | |
// Is WebXR available on this UA? | |
if (navigator.xr) { | |
// Request an XRDevice connected to the system. | |
navigator.xr.requestDevice().then(device => { | |
xrDevice = device | |
// If the device allows creation of exclusive sessions, | |
// set it as the target of the 'Enter XR' button. | |
device.supportsSession({ exclusive: true }).then(() => { | |
// Updates the button to start an XR session when clicked. | |
xrButton.addEventListener('click', onButtonClicked) | |
xrButton.innerHTML = 'Enter XR' | |
xrButton.disabled = false | |
}) | |
}) | |
} else { | |
log.message('This browser does not support the WebXR API.') | |
} | |
} | |
// Called when the user clicks the button to enter XR. If we don't have a | |
// session already we'll request one, and if we do we'll end it. | |
function onButtonClicked () { | |
if (!xrSession) { | |
xrDevice.requestSession({ exclusive: true }).then(onSessionStarted) | |
} else { | |
xrSession.end() | |
} | |
} | |
// Called when we've successfully acquired a XRSession. In response we | |
// will set up the necessary session state and kick off the frame loop. | |
function onSessionStarted (session) { | |
// Save session to global. | |
xrSession = session | |
xrButton.innerHTML = 'Exit XR' | |
// Listen for the sessions 'end' event so we can respond if the user | |
// or UA ends the session for any reason. | |
session.addEventListener('end', onSessionEnded) | |
// Create a WebGL context to render with, initialized to be compatible | |
// with the XRDisplay we're presenting to. | |
let canvas = document.createElement('canvas') | |
gl = canvas.getContext('webgl', { | |
compatibleXRDevice: session.device | |
}) | |
// Use the new WebGL context to create a XRWebGLLayer and set it as the | |
// sessions baseLayer. This allows any content rendered to the layer to | |
// be displayed on the XRDevice. | |
session.baseLayer = new window.XRWebGLLayer(session, gl) | |
// Get a frame of reference, which is required for querying poses. In | |
// this case an 'eyeLevel' frame of reference means that all poses will | |
// be relative to the location where the XRDevice was first detected. | |
session.requestFrameOfReference('eyeLevel').then(frameOfRef => { | |
xrFrameOfRef = frameOfRef | |
// Inform the session that we're ready to begin drawing. | |
session.requestAnimationFrame(onXRFrame) | |
}) | |
} | |
// // // Called when the user clicks the 'Exit XR' button. In response we end | |
// // // the session. | |
// // function onEndSession (session) { | |
// // session.end() | |
// // } | |
// Called either when the user has explicitly ended the session (like in | |
// onEndSession()) or when the UA has ended the session for any reason. | |
// At this point the session object is no longer usable and should be discarded. | |
function onSessionEnded (event) { | |
xrSession = null | |
xrButton.innerHTML = 'Enter VR' | |
// In this simple case discard the WebGL context too, since we're not | |
// rendering anything else to the screen with it. | |
gl = null | |
} | |
// Called every time the XRSession requests that a new frame be drawn. | |
function onXRFrame (t, frame) { | |
let session = frame.session | |
// Inform the session that we're ready for the next frame. | |
session.requestAnimationFrame(onXRFrame) | |
// Get the XRDevice pose relative to the Frame of Reference we created | |
// earlier. | |
let pose = frame.getDevicePose(xrFrameOfRef) | |
// Getting the pose may fail if, for example, tracking is lost. So we | |
// have to check to make sure that we got a valid pose before attempting | |
// to render with it. If not in this case we'll just leave the | |
// framebuffer cleared, so tracking loss means the scene will simply | |
// dissapear. | |
if (pose) { | |
// If we do have a valid pose, bind the WebGL layer's framebuffer, | |
// which is where any content to be displayed on the XRDevice must be | |
// rendered. | |
gl.bindFramebuffer(gl.FRAMEBUFFER, session.baseLayer.framebuffer) | |
// Update the clear color so that we can observe the color in the | |
// headset changing over time. | |
let time = Date.now() | |
gl.clearColor( | |
Math.cos(time / 2000), | |
Math.cos(time / 4000), | |
Math.cos(time / 6000), | |
1.0 | |
) | |
// Clear the framebuffer | |
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) | |
// Normally you'd loop through each of the views reported by the frame | |
// and draw them into the corresponding viewport here, but we're | |
// keeping this sample slim so we're not bothering to draw any | |
// geometry. | |
/* for (let view of frame.views) { | |
let viewport = session.baseLayer.getViewport(view); | |
gl.viewport(viewport.x, viewport.y, | |
viewport.width, viewport.height); | |
// Draw something. | |
} */ | |
} | |
} | |
// Start the XR application. | |
initXR() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> | |
<html> | |
<head> | |
<meta http-equiv="content-type" content="text/html; charset=UTF-8"> | |
<title>~/Documents/other/blocks/xr/first/demo.js.html</title> | |
<meta name="Generator" content="Vim/8.0"> | |
<meta name="plugin-version" content="vim7.4_v2"> | |
<meta name="syntax" content="javascript"> | |
<meta name="settings" content="use_css,pre_wrap,no_foldcolumn,expand_tabs,prevent_copy="> | |
<meta name="colorscheme" content="OceanicNext"> | |
<style type="text/css"> | |
<!-- | |
pre { white-space: pre-wrap; font-family: monospace; color: #c6c6c6; background-color: #262626; } | |
body { font-family: monospace; color: #c6c6c6; background-color: #262626; } | |
* { font-size: 1em; } | |
.String { color: #87d787; } | |
.Boolean { color: #ff875f; } | |
.Function { color: #5f87d7; } | |
.Structure { color: #d787d7; } | |
.javaScriptNumber { color: #ff875f; } | |
.Normal { color: #c6c6c6; background-color: #262626; padding-bottom: 1px; } | |
.Comment { color: #767676; } | |
.Special { color: #5fafaf; } | |
.Identifier { color: #ff5f5f; } | |
.Statement { color: #ff5f5f; font-weight: bold; } | |
.Conditional { color: #d787d7; } | |
.Label { color: #ffd75f; } | |
.Keyword { color: #d787d7; } | |
.Include { color: #5f87d7; } | |
.javaScriptBraces { color: #c6c6c6; } | |
--> | |
</style> | |
<script type='text/javascript'> | |
<!-- | |
--> | |
</script> | |
</head> | |
<body> | |
<pre id='vimCodeElement'> | |
<span class="Include">import</span> Log <span class="Include">from</span> <span class="String">'./log.js'</span> | |
<span class="Identifier">const</span> log <span class="Normal">=</span> Log<span class="Normal">()</span> | |
<span class="Comment">// XR globals.</span> | |
<span class="Identifier">let</span> xrButton <span class="Normal">=</span> document.<span class="Keyword">getElementById</span><span class="Normal">(</span><span class="String">'xr-button'</span><span class="Normal">)</span> | |
<span class="Identifier">let</span> xrDevice <span class="Normal">=</span> <span class="Boolean">null</span> | |
<span class="Identifier">let</span> xrSession <span class="Normal">=</span> <span class="Boolean">null</span> | |
<span class="Identifier">let</span> xrFrameOfRef <span class="Normal">=</span> <span class="Boolean">null</span> | |
<span class="Comment">// WebGL scene globals.</span> | |
<span class="Identifier">let</span> gl <span class="Normal">=</span> <span class="Boolean">null</span> | |
<span class="Comment">// Checks to see if WebXR is available and, if so, requests an XRDevice</span> | |
<span class="Comment">// that is connected to the system and tests it to ensure it supports the</span> | |
<span class="Comment">// desired session options.</span> | |
<span class="Keyword">function</span> <span class="Function">initXR</span> <span class="Normal">()</span> <span class="javaScriptBraces">{</span> | |
<span class="Comment">// Is WebXR available on this UA?</span> | |
<span class="Conditional">if</span> (<span class="Structure">navigator</span>.xr) <span class="javaScriptBraces">{</span> | |
<span class="Comment">// Request an XRDevice connected to the system.</span> | |
<span class="Structure">navigator</span>.xr.requestDevice<span class="Normal">()</span>.<span class="Keyword">then</span><span class="Normal">(</span><span class="Special">device</span> <span class="Statement">=></span> <span class="javaScriptBraces">{</span> | |
xrDevice <span class="Normal">=</span> device | |
<span class="Comment">// If the device allows creation of exclusive sessions,</span> | |
<span class="Comment">// set it as the target of the 'Enter XR' button.</span> | |
device.supportsSession<span class="Normal">(</span><span class="javaScriptBraces">{</span> <span class="Label">exclusive</span>: <span class="Boolean">true</span> <span class="javaScriptBraces">}</span><span class="Normal">)</span>.<span class="Keyword">then</span><span class="Normal">(()</span> <span class="Statement">=></span> <span class="javaScriptBraces">{</span> | |
<span class="Comment">// Updates the button to start an XR session when clicked.</span> | |
xrButton.<span class="Keyword">addEventListener</span><span class="Normal">(</span><span class="String">'click'</span><span class="Normal">,</span> onButtonClicked<span class="Normal">)</span> | |
xrButton.innerHTML <span class="Normal">=</span> <span class="String">'Enter XR'</span> | |
xrButton.disabled <span class="Normal">=</span> <span class="Boolean">false</span> | |
<span class="javaScriptBraces">}</span><span class="Normal">)</span> | |
<span class="javaScriptBraces">}</span><span class="Normal">)</span> | |
<span class="javaScriptBraces">}</span> <span class="Conditional">else</span> <span class="javaScriptBraces">{</span> | |
log.message<span class="Normal">(</span><span class="String">'This browser does not support the WebXR API.'</span><span class="Normal">)</span> | |
<span class="javaScriptBraces">}</span> | |
<span class="javaScriptBraces">}</span> | |
<span class="Comment">// Called when the user clicks the button to enter XR. If we don't have a</span> | |
<span class="Comment">// session already we'll request one, and if we do we'll end it.</span> | |
<span class="Keyword">function</span> <span class="Function">onButtonClicked</span> <span class="Normal">()</span> <span class="javaScriptBraces">{</span> | |
<span class="Conditional">if</span> (!xrSession) <span class="javaScriptBraces">{</span> | |
xrDevice.requestSession<span class="Normal">(</span><span class="javaScriptBraces">{</span> <span class="Label">exclusive</span>: <span class="Boolean">true</span> <span class="javaScriptBraces">}</span><span class="Normal">)</span>.<span class="Keyword">then</span><span class="Normal">(</span>onSessionStarted<span class="Normal">)</span> | |
<span class="javaScriptBraces">}</span> <span class="Conditional">else</span> <span class="javaScriptBraces">{</span> | |
xrSession.end<span class="Normal">()</span> | |
<span class="javaScriptBraces">}</span> | |
<span class="javaScriptBraces">}</span> | |
<span class="Comment">// Called when we've successfully acquired a XRSession. In response we</span> | |
<span class="Comment">// will set up the necessary session state and kick off the frame loop.</span> | |
<span class="Keyword">function</span> <span class="Function">onSessionStarted</span> <span class="Normal">(</span><span class="Special">session</span><span class="Normal">)</span> <span class="javaScriptBraces">{</span> | |
<span class="Comment">// Save session to global.</span> | |
xrSession <span class="Normal">=</span> session | |
xrButton.innerHTML <span class="Normal">=</span> <span class="String">'Exit XR'</span> | |
<span class="Comment">// Listen for the sessions 'end' event so we can respond if the user</span> | |
<span class="Comment">// or UA ends the session for any reason.</span> | |
session.<span class="Keyword">addEventListener</span><span class="Normal">(</span><span class="String">'end'</span><span class="Normal">,</span> onSessionEnded<span class="Normal">)</span> | |
<span class="Comment">// Create a WebGL context to render with, initialized to be compatible</span> | |
<span class="Comment">// with the XRDisplay we're presenting to.</span> | |
<span class="Identifier">let</span> canvas <span class="Normal">=</span> document.<span class="Keyword">createElement</span><span class="Normal">(</span><span class="String">'canvas'</span><span class="Normal">)</span> | |
gl <span class="Normal">=</span> canvas.getContext<span class="Normal">(</span><span class="String">'webgl'</span><span class="Normal">,</span> <span class="javaScriptBraces">{</span> | |
<span class="Label">compatibleXRDevice</span>: session.device | |
<span class="javaScriptBraces">}</span><span class="Normal">)</span> | |
<span class="Comment">// Use the new WebGL context to create a XRWebGLLayer and set it as the</span> | |
<span class="Comment">// sessions baseLayer. This allows any content rendered to the layer to</span> | |
<span class="Comment">// be displayed on the XRDevice.</span> | |
session.baseLayer <span class="Normal">=</span> <span class="Identifier">new</span> window.XRWebGLLayer<span class="Normal">(</span>session<span class="Normal">,</span> gl<span class="Normal">)</span> | |
<span class="Comment">// Get a frame of reference, which is required for querying poses. In</span> | |
<span class="Comment">// this case an 'eyeLevel' frame of reference means that all poses will</span> | |
<span class="Comment">// be relative to the location where the XRDevice was first detected.</span> | |
session.requestFrameOfReference<span class="Normal">(</span><span class="String">'eyeLevel'</span><span class="Normal">)</span>.<span class="Keyword">then</span><span class="Normal">(</span><span class="Special">frameOfRef</span> <span class="Statement">=></span> <span class="javaScriptBraces">{</span> | |
xrFrameOfRef <span class="Normal">=</span> frameOfRef | |
<span class="Comment">// Inform the session that we're ready to begin drawing.</span> | |
session.requestAnimationFrame<span class="Normal">(</span>onXRFrame<span class="Normal">)</span> | |
<span class="javaScriptBraces">}</span><span class="Normal">)</span> | |
<span class="javaScriptBraces">}</span> | |
<span class="Comment">// // // Called when the user clicks the 'Exit XR' button. In response we end</span> | |
<span class="Comment">// // // the session.</span> | |
<span class="Comment">// // function onEndSession (session) {</span> | |
<span class="Comment">// // session.end()</span> | |
<span class="Comment">// // }</span> | |
<span class="Comment">// Called either when the user has explicitly ended the session (like in</span> | |
<span class="Comment">// onEndSession()) or when the UA has ended the session for any reason.</span> | |
<span class="Comment">// At this point the session object is no longer usable and should be discarded.</span> | |
<span class="Keyword">function</span> <span class="Function">onSessionEnded</span> <span class="Normal">(</span><span class="Special">event</span><span class="Normal">)</span> <span class="javaScriptBraces">{</span> | |
xrSession <span class="Normal">=</span> <span class="Boolean">null</span> | |
xrButton.innerHTML <span class="Normal">=</span> <span class="String">'Enter VR'</span> | |
<span class="Comment">// In this simple case discard the WebGL context too, since we're not</span> | |
<span class="Comment">// rendering anything else to the screen with it.</span> | |
gl <span class="Normal">=</span> <span class="Boolean">null</span> | |
<span class="javaScriptBraces">}</span> | |
<span class="Comment">// Called every time the XRSession requests that a new frame be drawn.</span> | |
<span class="Keyword">function</span> <span class="Function">onXRFrame</span> <span class="Normal">(</span><span class="Special">t</span><span class="Normal">,</span><span class="Special"> frame</span><span class="Normal">)</span> <span class="javaScriptBraces">{</span> | |
<span class="Identifier">let</span> session <span class="Normal">=</span> frame.session | |
<span class="Comment">// Inform the session that we're ready for the next frame.</span> | |
session.requestAnimationFrame<span class="Normal">(</span>onXRFrame<span class="Normal">)</span> | |
<span class="Comment">// Get the XRDevice pose relative to the Frame of Reference we created</span> | |
<span class="Comment">// earlier.</span> | |
<span class="Identifier">let</span> pose <span class="Normal">=</span> frame.getDevicePose<span class="Normal">(</span>xrFrameOfRef<span class="Normal">)</span> | |
<span class="Comment">// Getting the pose may fail if, for example, tracking is lost. So we</span> | |
<span class="Comment">// have to check to make sure that we got a valid pose before attempting</span> | |
<span class="Comment">// to render with it. If not in this case we'll just leave the</span> | |
<span class="Comment">// framebuffer cleared, so tracking loss means the scene will simply</span> | |
<span class="Comment">// dissapear.</span> | |
<span class="Conditional">if</span> (pose) <span class="javaScriptBraces">{</span> | |
<span class="Comment">// If we do have a valid pose, bind the WebGL layer's framebuffer,</span> | |
<span class="Comment">// which is where any content to be displayed on the XRDevice must be</span> | |
<span class="Comment">// rendered.</span> | |
gl.bindFramebuffer<span class="Normal">(</span>gl.FRAMEBUFFER<span class="Normal">,</span> session.baseLayer.framebuffer<span class="Normal">)</span> | |
<span class="Comment">// Update the clear color so that we can observe the color in the</span> | |
<span class="Comment">// headset changing over time.</span> | |
<span class="Identifier">let</span> time <span class="Normal">=</span> <span class="Structure">Date</span>.<span class="Keyword">now</span><span class="Normal">()</span> | |
gl.clearColor<span class="Normal">(</span> | |
<span class="Structure">Math</span>.<span class="Keyword">cos</span><span class="Normal">(</span>time <span class="Normal">/</span> <span class="javaScriptNumber">2000</span><span class="Normal">)</span><span class="Normal">,</span> | |
<span class="Structure">Math</span>.<span class="Keyword">cos</span><span class="Normal">(</span>time <span class="Normal">/</span> <span class="javaScriptNumber">4000</span><span class="Normal">)</span><span class="Normal">,</span> | |
<span class="Structure">Math</span>.<span class="Keyword">cos</span><span class="Normal">(</span>time <span class="Normal">/</span> <span class="javaScriptNumber">6000</span><span class="Normal">)</span><span class="Normal">,</span> | |
<span class="javaScriptNumber">1.0</span> | |
<span class="Normal">)</span> | |
<span class="Comment">// Clear the framebuffer</span> | |
gl.<span class="Keyword">clear</span><span class="Normal">(</span>gl.COLOR_BUFFER_BIT <span class="Normal">|</span> gl.DEPTH_BUFFER_BIT<span class="Normal">)</span> | |
<span class="Comment">// Normally you'd loop through each of the views reported by the frame</span> | |
<span class="Comment">// and draw them into the corresponding viewport here, but we're</span> | |
<span class="Comment">// keeping this sample slim so we're not bothering to draw any</span> | |
<span class="Comment">// geometry.</span> | |
<span class="Comment">/* for (let view of frame.views) {</span> | |
<span class="Comment"> let viewport = session.baseLayer.getViewport(view);</span> | |
<span class="Comment"> gl.viewport(viewport.x, viewport.y,</span> | |
<span class="Comment"> viewport.width, viewport.height);</span> | |
<span class="Comment"> // Draw something.</span> | |
<span class="Comment"> } */</span> | |
<span class="javaScriptBraces">}</span> | |
<span class="javaScriptBraces">}</span> | |
<span class="Comment">// Start the XR application.</span> | |
initXR<span class="Normal">()</span> | |
</pre> | |
</body> | |
</html> | |
<!-- vim: set foldmethod=manual : --> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
*{box-sizing:border-box} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
!function(e){function n(t){if(l[t])return l[t].exports;var c=l[t]={i:t,l:!1,exports:{}};return e[t].call(c.exports,c,c.exports,n),c.l=!0,c.exports}var l={};n.m=e,n.c=l,n.i=function(e){return e},n.d=function(e,l,t){n.o(e,l)||Object.defineProperty(e,l,{configurable:!1,enumerable:!0,get:t})},n.n=function(e){var l=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(l,"a",l),l},n.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},n.p="",n(n.s=2)}([function(module,exports,__webpack_require__){"use strict";eval("\n\nvar _log = __webpack_require__(1);\n\nvar _log2 = _interopRequireDefault(_log);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nvar log = (0, _log2.default)();\n\n// XR globals.\nvar xrButton = document.getElementById('xr-button');\nvar xrDevice = null;\nvar xrSession = null;\nvar xrFrameOfRef = null;\n\n// WebGL scene globals.\nvar gl = null;\n\n// Checks to see if WebXR is available and, if so, requests an XRDevice\n// that is connected to the system and tests it to ensure it supports the\n// desired session options.\nfunction initXR() {\n // Is WebXR available on this UA?\n if (navigator.xr) {\n // Request an XRDevice connected to the system.\n navigator.xr.requestDevice().then(function (device) {\n xrDevice = device;\n // If the device allows creation of exclusive sessions,\n // set it as the target of the 'Enter XR' button.\n device.supportsSession({ exclusive: true }).then(function () {\n // Updates the button to start an XR session when clicked.\n xrButton.addEventListener('click', onButtonClicked);\n xrButton.innerHTML = 'Enter XR';\n xrButton.disabled = false;\n });\n });\n } else {\n log.message('This browser does not support the WebXR API.');\n }\n}\n\n// Called when the user clicks the button to enter XR. If we don't have a\n// session already we'll request one, and if we do we'll end it.\nfunction onButtonClicked() {\n if (!xrSession) {\n xrDevice.requestSession({ exclusive: true }).then(onSessionStarted);\n } else {\n xrSession.end();\n }\n}\n\n// Called when we've successfully acquired a XRSession. In response we\n// will set up the necessary session state and kick off the frame loop.\nfunction onSessionStarted(session) {\n // Save session to global.\n xrSession = session;\n xrButton.innerHTML = 'Exit XR';\n\n // Listen for the sessions 'end' event so we can respond if the user\n // or UA ends the session for any reason.\n session.addEventListener('end', onSessionEnded);\n\n // Create a WebGL context to render with, initialized to be compatible\n // with the XRDisplay we're presenting to.\n var canvas = document.createElement('canvas');\n gl = canvas.getContext('webgl', {\n compatibleXRDevice: session.device\n });\n\n // Use the new WebGL context to create a XRWebGLLayer and set it as the\n // sessions baseLayer. This allows any content rendered to the layer to\n // be displayed on the XRDevice.\n session.baseLayer = new window.XRWebGLLayer(session, gl);\n\n // Get a frame of reference, which is required for querying poses. In\n // this case an 'eyeLevel' frame of reference means that all poses will\n // be relative to the location where the XRDevice was first detected.\n session.requestFrameOfReference('eyeLevel').then(function (frameOfRef) {\n xrFrameOfRef = frameOfRef;\n // Inform the session that we're ready to begin drawing.\n session.requestAnimationFrame(onXRFrame);\n });\n}\n\n// // // Called when the user clicks the 'Exit XR' button. In response we end\n// // // the session.\n// // function onEndSession (session) {\n// // session.end()\n// // }\n\n// Called either when the user has explicitly ended the session (like in\n// onEndSession()) or when the UA has ended the session for any reason.\n// At this point the session object is no longer usable and should be discarded.\nfunction onSessionEnded(event) {\n xrSession = null;\n xrButton.innerHTML = 'Enter VR';\n // In this simple case discard the WebGL context too, since we're not\n // rendering anything else to the screen with it.\n gl = null;\n}\n\n// Called every time the XRSession requests that a new frame be drawn.\nfunction onXRFrame(t, frame) {\n var session = frame.session;\n\n // Inform the session that we're ready for the next frame.\n session.requestAnimationFrame(onXRFrame);\n\n // Get the XRDevice pose relative to the Frame of Reference we created\n // earlier.\n var pose = frame.getDevicePose(xrFrameOfRef);\n\n // Getting the pose may fail if, for example, tracking is lost. So we\n // have to check to make sure that we got a valid pose before attempting\n // to render with it. If not in this case we'll just leave the\n // framebuffer cleared, so tracking loss means the scene will simply\n // dissapear.\n if (pose) {\n // If we do have a valid pose, bind the WebGL layer's framebuffer,\n // which is where any content to be displayed on the XRDevice must be\n // rendered.\n gl.bindFramebuffer(gl.FRAMEBUFFER, session.baseLayer.framebuffer);\n\n // Update the clear color so that we can observe the color in the\n // headset changing over time.\n var time = Date.now();\n gl.clearColor(Math.cos(time / 2000), Math.cos(time / 4000), Math.cos(time / 6000), 1.0);\n\n // Clear the framebuffer\n gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);\n\n // Normally you'd loop through each of the views reported by the frame\n // and draw them into the corresponding viewport here, but we're\n // keeping this sample slim so we're not bothering to draw any\n // geometry.\n /* for (let view of frame.views) {\n let viewport = session.baseLayer.getViewport(view);\n gl.viewport(viewport.x, viewport.y,\n viewport.width, viewport.height);\n // Draw something.\n } */\n }\n}\n\n// Start the XR application.\ninitXR();//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMC5qcyIsInNvdXJjZXMiOlsid2VicGFjazovLy9kZW1vLmpzPzk2MDAiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IExvZyBmcm9tICcuL2xvZy5qcydcbmNvbnN0IGxvZyA9IExvZygpXG5cbi8vIFhSIGdsb2JhbHMuXG5sZXQgeHJCdXR0b24gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgneHItYnV0dG9uJylcbmxldCB4ckRldmljZSA9IG51bGxcbmxldCB4clNlc3Npb24gPSBudWxsXG5sZXQgeHJGcmFtZU9mUmVmID0gbnVsbFxuXG4vLyBXZWJHTCBzY2VuZSBnbG9iYWxzLlxubGV0IGdsID0gbnVsbFxuXG4vLyBDaGVja3MgdG8gc2VlIGlmIFdlYlhSIGlzIGF2YWlsYWJsZSBhbmQsIGlmIHNvLCByZXF1ZXN0cyBhbiBYUkRldmljZVxuLy8gdGhhdCBpcyBjb25uZWN0ZWQgdG8gdGhlIHN5c3RlbSBhbmQgdGVzdHMgaXQgdG8gZW5zdXJlIGl0IHN1cHBvcnRzIHRoZVxuLy8gZGVzaXJlZCBzZXNzaW9uIG9wdGlvbnMuXG5mdW5jdGlvbiBpbml0WFIgKCkge1xuICAvLyBJcyBXZWJYUiBhdmFpbGFibGUgb24gdGhpcyBVQT9cbiAgaWYgKG5hdmlnYXRvci54cikge1xuICAgIC8vIFJlcXVlc3QgYW4gWFJEZXZpY2UgY29ubmVjdGVkIHRvIHRoZSBzeXN0ZW0uXG4gICAgbmF2aWdhdG9yLnhyLnJlcXVlc3REZXZpY2UoKS50aGVuKGRldmljZSA9PiB7XG4gICAgICB4ckRldmljZSA9IGRldmljZVxuICAgICAgLy8gSWYgdGhlIGRldmljZSBhbGxvd3MgY3JlYXRpb24gb2YgZXhjbHVzaXZlIHNlc3Npb25zLFxuICAgICAgLy8gc2V0IGl0IGFzIHRoZSB0YXJnZXQgb2YgdGhlICdFbnRlciBYUicgYnV0dG9uLlxuICAgICAgZGV2aWNlLnN1cHBvcnRzU2Vzc2lvbih7IGV4Y2x1c2l2ZTogdHJ1ZSB9KS50aGVuKCgpID0+IHtcbiAgICAgICAgLy8gVXBkYXRlcyB0aGUgYnV0dG9uIHRvIHN0YXJ0IGFuIFhSIHNlc3Npb24gd2hlbiBjbGlja2VkLlxuICAgICAgICB4ckJ1dHRvbi5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIG9uQnV0dG9uQ2xpY2tlZClcbiAgICAgICAgeHJCdXR0b24uaW5uZXJIVE1MID0gJ0VudGVyIFhSJ1xuICAgICAgICB4ckJ1dHRvbi5kaXNhYmxlZCA9IGZhbHNlXG4gICAgICB9KVxuICAgIH0pXG4gIH0gZWxzZSB7XG4gICAgbG9nLm1lc3NhZ2UoJ1RoaXMgYnJvd3NlciBkb2VzIG5vdCBzdXBwb3J0IHRoZSBXZWJYUiBBUEkuJylcbiAgfVxufVxuXG4vLyBDYWxsZWQgd2hlbiB0aGUgdXNlciBjbGlja3MgdGhlIGJ1dHRvbiB0byBlbnRlciBYUi4gSWYgd2UgZG9uJ3QgaGF2ZSBhXG4vLyBzZXNzaW9uIGFscmVhZHkgd2UnbGwgcmVxdWVzdCBvbmUsIGFuZCBpZiB3ZSBkbyB3ZSdsbCBlbmQgaXQuXG5mdW5jdGlvbiBvbkJ1dHRvbkNsaWNrZWQgKCkge1xuICBpZiAoIXhyU2Vzc2lvbikge1xuICAgIHhyRGV2aWNlLnJlcXVlc3RTZXNzaW9uKHsgZXhjbHVzaXZlOiB0cnVlIH0pLnRoZW4ob25TZXNzaW9uU3RhcnRlZClcbiAgfSBlbHNlIHtcbiAgICB4clNlc3Npb24uZW5kKClcbiAgfVxufVxuXG4vLyBDYWxsZWQgd2hlbiB3ZSd2ZSBzdWNjZXNzZnVsbHkgYWNxdWlyZWQgYSBYUlNlc3Npb24uIEluIHJlc3BvbnNlIHdlXG4vLyB3aWxsIHNldCB1cCB0aGUgbmVjZXNzYXJ5IHNlc3Npb24gc3RhdGUgYW5kIGtpY2sgb2ZmIHRoZSBmcmFtZSBsb29wLlxuZnVuY3Rpb24gb25TZXNzaW9uU3RhcnRlZCAoc2Vzc2lvbikge1xuICAvLyBTYXZlIHNlc3Npb24gdG8gZ2xvYmFsLlxuICB4clNlc3Npb24gPSBzZXNzaW9uXG4gIHhyQnV0dG9uLmlubmVySFRNTCA9ICdFeGl0IFhSJ1xuXG4gIC8vIExpc3RlbiBmb3IgdGhlIHNlc3Npb25zICdlbmQnIGV2ZW50IHNvIHdlIGNhbiByZXNwb25kIGlmIHRoZSB1c2VyXG4gIC8vIG9yIFVBIGVuZHMgdGhlIHNlc3Npb24gZm9yIGFueSByZWFzb24uXG4gIHNlc3Npb24uYWRkRXZlbnRMaXN0ZW5lcignZW5kJywgb25TZXNzaW9uRW5kZWQpXG5cbiAgLy8gQ3JlYXRlIGEgV2ViR0wgY29udGV4dCB0byByZW5kZXIgd2l0aCwgaW5pdGlhbGl6ZWQgdG8gYmUgY29tcGF0aWJsZVxuICAvLyB3aXRoIHRoZSBYUkRpc3BsYXkgd2UncmUgcHJlc2VudGluZyB0by5cbiAgbGV0IGNhbnZhcyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2NhbnZhcycpXG4gIGdsID0gY2FudmFzLmdldENvbnRleHQoJ3dlYmdsJywge1xuICAgIGNvbXBhdGlibGVYUkRldmljZTogc2Vzc2lvbi5kZXZpY2VcbiAgfSlcblxuICAvLyBVc2UgdGhlIG5ldyBXZWJHTCBjb250ZXh0IHRvIGNyZWF0ZSBhIFhSV2ViR0xMYXllciBhbmQgc2V0IGl0IGFzIHRoZVxuICAvLyBzZXNzaW9ucyBiYXNlTGF5ZXIuIFRoaXMgYWxsb3dzIGFueSBjb250ZW50IHJlbmRlcmVkIHRvIHRoZSBsYXllciB0b1xuICAvLyBiZSBkaXNwbGF5ZWQgb24gdGhlIFhSRGV2aWNlLlxuICBzZXNzaW9uLmJhc2VMYXllciA9IG5ldyB3aW5kb3cuWFJXZWJHTExheWVyKHNlc3Npb24sIGdsKVxuXG4gIC8vIEdldCBhIGZyYW1lIG9mIHJlZmVyZW5jZSwgd2hpY2ggaXMgcmVxdWlyZWQgZm9yIHF1ZXJ5aW5nIHBvc2VzLiBJblxuICAvLyB0aGlzIGNhc2UgYW4gJ2V5ZUxldmVsJyBmcmFtZSBvZiByZWZlcmVuY2UgbWVhbnMgdGhhdCBhbGwgcG9zZXMgd2lsbFxuICAvLyBiZSByZWxhdGl2ZSB0byB0aGUgbG9jYXRpb24gd2hlcmUgdGhlIFhSRGV2aWNlIHdhcyBmaXJzdCBkZXRlY3RlZC5cbiAgc2Vzc2lvbi5yZXF1ZXN0RnJhbWVPZlJlZmVyZW5jZSgnZXllTGV2ZWwnKS50aGVuKGZyYW1lT2ZSZWYgPT4ge1xuICAgIHhyRnJhbWVPZlJlZiA9IGZyYW1lT2ZSZWZcbiAgICAvLyBJbmZvcm0gdGhlIHNlc3Npb24gdGhhdCB3ZSdyZSByZWFkeSB0byBiZWdpbiBkcmF3aW5nLlxuICAgIHNlc3Npb24ucmVxdWVzdEFuaW1hdGlvbkZyYW1lKG9uWFJGcmFtZSlcbiAgfSlcbn1cblxuLy8gLy8gLy8gQ2FsbGVkIHdoZW4gdGhlIHVzZXIgY2xpY2tzIHRoZSAnRXhpdCBYUicgYnV0dG9uLiBJbiByZXNwb25zZSB3ZSBlbmRcbi8vIC8vIC8vIHRoZSBzZXNzaW9uLlxuLy8gLy8gZnVuY3Rpb24gb25FbmRTZXNzaW9uIChzZXNzaW9uKSB7XG4vLyAvLyAgIHNlc3Npb24uZW5kKClcbi8vIC8vIH1cblxuLy8gQ2FsbGVkIGVpdGhlciB3aGVuIHRoZSB1c2VyIGhhcyBleHBsaWNpdGx5IGVuZGVkIHRoZSBzZXNzaW9uIChsaWtlIGluXG4vLyBvbkVuZFNlc3Npb24oKSkgb3Igd2hlbiB0aGUgVUEgaGFzIGVuZGVkIHRoZSBzZXNzaW9uIGZvciBhbnkgcmVhc29uLlxuLy8gQXQgdGhpcyBwb2ludCB0aGUgc2Vzc2lvbiBvYmplY3QgaXMgbm8gbG9uZ2VyIHVzYWJsZSBhbmQgc2hvdWxkIGJlIGRpc2NhcmRlZC5cbmZ1bmN0aW9uIG9uU2Vzc2lvbkVuZGVkIChldmVudCkge1xuICB4clNlc3Npb24gPSBudWxsXG4gIHhyQnV0dG9uLmlubmVySFRNTCA9ICdFbnRlciBWUidcbiAgLy8gSW4gdGhpcyBzaW1wbGUgY2FzZSBkaXNjYXJkIHRoZSBXZWJHTCBjb250ZXh0IHRvbywgc2luY2Ugd2UncmUgbm90XG4gIC8vIHJlbmRlcmluZyBhbnl0aGluZyBlbHNlIHRvIHRoZSBzY3JlZW4gd2l0aCBpdC5cbiAgZ2wgPSBudWxsXG59XG5cbi8vIENhbGxlZCBldmVyeSB0aW1lIHRoZSBYUlNlc3Npb24gcmVxdWVzdHMgdGhhdCBhIG5ldyBmcmFtZSBiZSBkcmF3bi5cbmZ1bmN0aW9uIG9uWFJGcmFtZSAodCwgZnJhbWUpIHtcbiAgbGV0IHNlc3Npb24gPSBmcmFtZS5zZXNzaW9uXG5cbiAgLy8gSW5mb3JtIHRoZSBzZXNzaW9uIHRoYXQgd2UncmUgcmVhZHkgZm9yIHRoZSBuZXh0IGZyYW1lLlxuICBzZXNzaW9uLnJlcXVlc3RBbmltYXRpb25GcmFtZShvblhSRnJhbWUpXG5cbiAgLy8gR2V0IHRoZSBYUkRldmljZSBwb3NlIHJlbGF0aXZlIHRvIHRoZSBGcmFtZSBvZiBSZWZlcmVuY2Ugd2UgY3JlYXRlZFxuICAvLyBlYXJsaWVyLlxuICBsZXQgcG9zZSA9IGZyYW1lLmdldERldmljZVBvc2UoeHJGcmFtZU9mUmVmKVxuXG4gIC8vIEdldHRpbmcgdGhlIHBvc2UgbWF5IGZhaWwgaWYsIGZvciBleGFtcGxlLCB0cmFja2luZyBpcyBsb3N0LiBTbyB3ZVxuICAvLyBoYXZlIHRvIGNoZWNrIHRvIG1ha2Ugc3VyZSB0aGF0IHdlIGdvdCBhIHZhbGlkIHBvc2UgYmVmb3JlIGF0dGVtcHRpbmdcbiAgLy8gdG8gcmVuZGVyIHdpdGggaXQuIElmIG5vdCBpbiB0aGlzIGNhc2Ugd2UnbGwganVzdCBsZWF2ZSB0aGVcbiAgLy8gZnJhbWVidWZmZXIgY2xlYXJlZCwgc28gdHJhY2tpbmcgbG9zcyBtZWFucyB0aGUgc2NlbmUgd2lsbCBzaW1wbHlcbiAgLy8gZGlzc2FwZWFyLlxuICBpZiAocG9zZSkge1xuICAgIC8vIElmIHdlIGRvIGhhdmUgYSB2YWxpZCBwb3NlLCBiaW5kIHRoZSBXZWJHTCBsYXllcidzIGZyYW1lYnVmZmVyLFxuICAgIC8vIHdoaWNoIGlzIHdoZXJlIGFueSBjb250ZW50IHRvIGJlIGRpc3BsYXllZCBvbiB0aGUgWFJEZXZpY2UgbXVzdCBiZVxuICAgIC8vIHJlbmRlcmVkLlxuICAgIGdsLmJpbmRGcmFtZWJ1ZmZlcihnbC5GUkFNRUJVRkZFUiwgc2Vzc2lvbi5iYXNlTGF5ZXIuZnJhbWVidWZmZXIpXG5cbiAgICAvLyBVcGRhdGUgdGhlIGNsZWFyIGNvbG9yIHNvIHRoYXQgd2UgY2FuIG9ic2VydmUgdGhlIGNvbG9yIGluIHRoZVxuICAgIC8vIGhlYWRzZXQgY2hhbmdpbmcgb3ZlciB0aW1lLlxuICAgIGxldCB0aW1lID0gRGF0ZS5ub3coKVxuICAgIGdsLmNsZWFyQ29sb3IoXG4gICAgICBNYXRoLmNvcyh0aW1lIC8gMjAwMCksXG4gICAgICBNYXRoLmNvcyh0aW1lIC8gNDAwMCksXG4gICAgICBNYXRoLmNvcyh0aW1lIC8gNjAwMCksXG4gICAgICAxLjBcbiAgICApXG5cbiAgICAvLyBDbGVhciB0aGUgZnJhbWVidWZmZXJcbiAgICBnbC5jbGVhcihnbC5DT0xPUl9CVUZGRVJfQklUIHwgZ2wuREVQVEhfQlVGRkVSX0JJVClcblxuICAgIC8vIE5vcm1hbGx5IHlvdSdkIGxvb3AgdGhyb3VnaCBlYWNoIG9mIHRoZSB2aWV3cyByZXBvcnRlZCBieSB0aGUgZnJhbWVcbiAgICAvLyBhbmQgZHJhdyB0aGVtIGludG8gdGhlIGNvcnJlc3BvbmRpbmcgdmlld3BvcnQgaGVyZSwgYnV0IHdlJ3JlXG4gICAgLy8ga2VlcGluZyB0aGlzIHNhbXBsZSBzbGltIHNvIHdlJ3JlIG5vdCBib3RoZXJpbmcgdG8gZHJhdyBhbnlcbiAgICAvLyBnZW9tZXRyeS5cbiAgICAvKiBmb3IgKGxldCB2aWV3IG9mIGZyYW1lLnZpZXdzKSB7XG4gICAgICAgICAgICBsZXQgdmlld3BvcnQgPSBzZXNzaW9uLmJhc2VMYXllci5nZXRWaWV3cG9ydCh2aWV3KTtcbiAgICAgICAgICAgIGdsLnZpZXdwb3J0KHZpZXdwb3J0LngsIHZpZXdwb3J0LnksXG4gICAgICAgICAgICAgICAgICAgICAgICB2aWV3cG9ydC53aWR0aCwgdmlld3BvcnQuaGVpZ2h0KTtcbiAgICAgICAgICAgIC8vIERyYXcgc29tZXRoaW5nLlxuICAgICAgICAgIH0gKi9cbiAgfVxufVxuXG4vLyBTdGFydCB0aGUgWFIgYXBwbGljYXRpb24uXG5pbml0WFIoKVxuXG5cblxuLy8gV0VCUEFDSyBGT09URVIgLy9cbi8vIGRlbW8uanMiXSwibWFwcGluZ3MiOiI7O0FBQUE7QUFDQTs7Ozs7QUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFEQTtBQUNBO0FBR0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQU1BO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7OztBQU1BO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///0\n")},function(module,exports,__webpack_require__){"use strict";eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nvar log = function log() {\n window.logMessages = [];\n\n var print = function print() {\n document.querySelector('.logs').innerHTML = window.logMessages.slice(-10).map(function (d) {\n return '<li>' + d.s + ' ' + (d.e ? d.e : '') + '</li>';\n }).join('');\n };\n\n return {\n message: function message(s) {\n window.logMessages.push({ s: s });\n print();\n },\n error: function error(s, e) {\n window.logMessages.push({ s: s, e: e });\n print();\n }\n };\n};\n\nexports.default = log;//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMS5qcyIsInNvdXJjZXMiOlsid2VicGFjazovLy9sb2cuanM/YjgzZiJdLCJzb3VyY2VzQ29udGVudCI6WyJjb25zdCBsb2cgPSAoKSA9PiB7XG4gIHdpbmRvdy5sb2dNZXNzYWdlcyA9IFtdXG5cbiAgY29uc3QgcHJpbnQgPSAoKSA9PiB7XG4gICAgZG9jdW1lbnQucXVlcnlTZWxlY3RvcignLmxvZ3MnKS5pbm5lckhUTUwgPSB3aW5kb3cubG9nTWVzc2FnZXNcbiAgICAgIC5zbGljZSgtMTApXG4gICAgICAubWFwKGQgPT4ge1xuICAgICAgICByZXR1cm4gYDxsaT4ke2Quc30gJHtkLmUgPyBkLmUgOiAnJ308L2xpPmBcbiAgICAgIH0pXG4gICAgICAuam9pbignJylcbiAgfVxuXG4gIHJldHVybiB7XG4gICAgbWVzc2FnZSAocykge1xuICAgICAgd2luZG93LmxvZ01lc3NhZ2VzLnB1c2goeyBzIH0pXG4gICAgICBwcmludCgpXG4gICAgfSxcblxuICAgIGVycm9yIChzLCBlKSB7XG4gICAgICB3aW5kb3cubG9nTWVzc2FnZXMucHVzaCh7IHMsIGUgfSlcbiAgICAgIHByaW50KClcbiAgICB9XG4gIH1cbn1cblxuZXhwb3J0IGRlZmF1bHQgbG9nXG5cblxuXG4vLyBXRUJQQUNLIEZPT1RFUiAvL1xuLy8gbG9nLmpzIl0sIm1hcHBpbmdzIjoiOzs7OztBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFHQTtBQUNBO0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQVRBO0FBV0E7QUFDQTtBQUNBIiwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///1\n")},function(module,exports,__webpack_require__){"use strict";eval("\n\n__webpack_require__(0);//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMi5qcyIsInNvdXJjZXMiOlsid2VicGFjazovLy9zY3JpcHQuanM/OWE5NSJdLCJzb3VyY2VzQ29udGVudCI6WyJyZXF1aXJlKCcuL2RlbW8uanMnKVxuXG5cblxuLy8gV0VCUEFDSyBGT09URVIgLy9cbi8vIHNjcmlwdC5qcyJdLCJtYXBwaW5ncyI6Ijs7QUFBQSIsInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///2\n")}]); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<title>blockup</title> | |
<meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"> | |
<link href='dist.css' rel='stylesheet' /> | |
<!-- Origin Trial Token, feature = WebXR Device API, origin = https://ngrok.io, expires = 2018-07-05 --> | |
<meta http-equiv="origin-trial" data-feature="WebXR Device API" data-expires="2018-07-05" content="An7UhyulDMd1aXHYJY/4aBUXSPnff/LpJhr8H3FW/EKLJb7GyuwH8DWrg+W6AYIxNX6f7vxJySrMcX6OkfgQuAYAAABNeyJvcmlnaW4iOiJodHRwczovL25ncm9rLmlvOjQ0MyIsImZlYXR1cmUiOiJXZWJYUkRldmljZSIsImV4cGlyeSI6MTUzMDgzNDIzNH0="> | |
<!-- <script src='https://cdn.jsdelivr.net/npm/webxr-polyfill@latest/build/webxr-polyfill.js'></script> | |
<script>var polyfill = new WebXRPolyfill()</script> | |
--> | |
<body> | |
<h1>hello</h1> | |
<ul class='logs' /> | |
<button id="xr-button" disabled>XR not found</button> | |
<script src='dist.js'></script> | |
</body> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const log = () => { | |
window.logMessages = [] | |
const print = () => { | |
document.querySelector('.logs').innerHTML = window.logMessages | |
.slice(-10) | |
.map(d => { | |
return `<li>${d.s} ${d.e ? d.e : ''}</li>` | |
}) | |
.join('') | |
} | |
return { | |
message (s) { | |
window.logMessages.push({ s }) | |
print() | |
}, | |
error (s, e) { | |
window.logMessages.push({ s, e }) | |
print() | |
} | |
} | |
} | |
export default log |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Log from './log.js' | |
import OnDrawFrame from './onDrawFrame.js' | |
const log = Log() | |
// TODO: review async functions | |
// TODO: check correct error handling setup | |
let device | |
let session | |
let frameOfRef | |
let onDrawFrame | |
let glCanvas | |
let gl | |
// 1 - Request an XR device. | |
// 2 - If a device is available, application advertises XR functionality to the user. | |
// 3 - Request an exclusive XR session from the device in response to a user-activation event. | |
// 4 - Setup necessary session state and kick off the frame loop. | |
// 5 - Use the session to run a render loop that produces graphical frames to be displayed on the XR device. | |
// 6 - Continue producing frames until the user indicates that they wish to exit XR mode. | |
// 7 - End the XR session. | |
// 3 - Request an exclusive XR session from the device in response to a user-activation event. | |
const beginXRSession = () => { | |
device | |
.requestSession({ exclusive: true }) | |
.then(xrSession => { | |
log.message('session started') | |
session = xrSession | |
onSessionStarted() | |
// Use a WebGL context as a base layer. | |
// xrSession.baseLayer = new XRWebGLLayer(session, gl); | |
// Start the render loop | |
}) | |
.catch(err => { | |
log.error('Could not request desired session', err) | |
}) | |
} | |
// 4 - Setup necessary session state and kick off the frame loop. | |
const onSessionStarted = () => { | |
// Get a frame of reference, which is required for querying poses. In | |
// this case an 'eyeLevel' frame of reference means that all poses will | |
// be relative to the location where the XRDevice was first detected. | |
session | |
.requestFrameOfReference('eyeLevel') | |
.then(xrFrameOfRef => { | |
log.message('frame of ref acquired') | |
frameOfRef = xrFrameOfRef | |
}) | |
.then(setupWebGLLayer) // Create a compatible XRWebGLLayer. | |
.then(() => { | |
// Start the render loop. | |
onDrawFrame = OnDrawFrame({ session, frameOfRef, gl, glCanvas, log }) | |
session.requestAnimationFrame(onDrawFrame) | |
}) | |
.catch(err => { | |
log.error('Error onSessionStarted', err) | |
}) | |
} | |
const setupWebGLLayer = () => { | |
glCanvas = document.createElement('canvas') | |
gl = glCanvas.getContext('webgl') | |
// Make sure the canvas context we want to use is compatible with the device. | |
return gl | |
.setCompatibleXRDevice(device) | |
.then(() => { | |
log.message('setting baseLayer') | |
// The content that will be shown on the device | |
// is defined by the session's baseLayer. | |
session.baseLayer = new window.XRWebGLLayer(session, gl) | |
}) | |
.catch(err => { | |
log.error('Error setupWebGLLayer', err) | |
}) | |
} | |
if (navigator.xr) { | |
// | |
// 1 - Request an XR device. | |
// | |
navigator.xr | |
.requestDevice() | |
.then(xrDevice => { | |
xrDevice | |
.supportsSession({ exclusive: true }) | |
.then(() => { | |
// | |
// 2 - If a device is available, application advertises XR functionality to the user. | |
// | |
device = xrDevice | |
const button = document.createElement('button') | |
button.innerHTML = 'Enter VR' | |
button.addEventListener('click', beginXRSession) | |
document.body.appendChild(button) | |
}) | |
.catch(err => { | |
log.error('Could not support desired session', err) | |
}) | |
}) | |
.catch(err => { | |
if (err.name === 'NotFoundError') { | |
// No XRDevices available. | |
log.error('No XR devices available:', err) | |
} else { | |
// An error occurred while requesting an XRDevice. | |
log.error('Requesting XR device failed:', err) | |
} | |
}) | |
} else { | |
log.message('This browser does not support the WebXR API.') | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const onDrawFrame = ({ session, frameOfRef, gl, glCanvas, log }) => ( | |
timestamp, | |
xrFrame | |
) => { | |
// Do we have an active session? | |
if (session) { | |
log.message('found active session') | |
let pose = xrFrame.getDevicePose(frameOfRef) | |
gl.bindFramebuffer(session.baseLayer.framebuffer) | |
for (let view of xrFrame.views) { | |
let viewport = session.baseLayer.getViewport(view) | |
gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height) | |
log.message('drawing gl viewport') | |
drawScene(view, pose) | |
} | |
// Request the next animation callback | |
// session.requestAnimationFrame(onDrawFrame) | |
} else { | |
// No session available, so render a default mono view. | |
gl.viewport(0, 0, glCanvas.width, glCanvas.height) | |
// drawScene() | |
// Request the next window callback | |
window.requestAnimationFrame(onDrawFrame) | |
} | |
} | |
const drawScene = (view, pose) => { | |
let viewMatrix = null | |
let projectionMatrix = null | |
if (view) { | |
viewMatrix = pose.getViewMatrix(view) | |
projectionMatrix = view.projectionMatrix | |
} else { | |
viewMatrix = defaultViewMatrix | |
projectionMatrix = defaultProjectionMatrix | |
} | |
// Set uniforms as appropriate for shaders being used | |
// Draw Scene | |
} | |
export default onDrawFrame |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"standard": { | |
"globals": [ | |
] | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require('./demo.js') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
* | |
box-sizing border-box |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let xrDevice = null | |
async function onXRAvailable (device) { | |
xrDevice = device | |
// Most (but not all) XRDevices are capable of granting exclusive access to | |
// the device, which is necessary to show imagery in a headset. If the device | |
// has that capability the page will want to add an "Enter VR" button (similar | |
// to "Enter Fullscreen") that triggers the page to begin showing imagery on | |
// the headset. | |
xrDevice | |
.supportsSession({ exclusive: true }) | |
.then(() => { | |
var enterXrBtn = document.createElement('button') | |
enterXrBtn.innerHTML = 'Enter VR' | |
enterXrBtn.addEventListener('click', beginXRSession) | |
document.body.appendChild(enterVrBtn) | |
}) | |
.catch(reason => { | |
console.log('Session not supported: ' + reason) | |
}) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment