diff --git a/ew.js b/ew.js index e8e3b03..d0ed33c 100644 --- a/ew.js +++ b/ew.js @@ -128,7 +128,7 @@ const xrState = (() => { result.msDepthTex = _makeTypedArray(Uint32Array, 1); result.aaEnabled = _makeTypedArray(Uint32Array, 1); result.fakeVrDisplayEnabled = _makeTypedArray(Uint32Array, 1); - result.blobId = _makeTypedArray(Uint32Array, 1); + // result.blobId = _makeTypedArray(Uint32Array, 1); return result; })(); @@ -144,16 +144,6 @@ customElements.define('xr-engine-template', XREngineTemplate, { extends: 'template', }); -(async () => { - -navigator.serviceWorker.register('/sw.js'); - -if (navigator.serviceWorker.controller) { - GlobalContext.loadPromise.resolve(); -} else { - window.location.reload(); -} - ['keydown', 'keyup', 'keypress', 'paste'].forEach(type => { window.addEventListener(type, e => { const event = { @@ -183,42 +173,6 @@ if (navigator.serviceWorker.controller) { } }); }); -['mousedown', 'mouseup', 'click', 'dblclick', 'mousemove', 'wheel'].forEach(type => { - window.addEventListener(type, e => { - const event = { - altKey: e.altKey, - button: e.button, - buttons: e.buttons, - clientX: e.clientX, - clientY: e.clientY, - ctrlKey: e.ctrlKey, - deltaMode: e.deltaMode, - deltaX: e.deltaX, - deltaY: e.deltaY, - deltaZ: e.deltaZ, - // detail: e.detail, - layerX: e.layerX, - layerY: e.layerY, - metaKey: e.metaKey, - movementX: e.movementX, - movementY: e.movementY, - offsetX: e.offsetX, - offsetY: e.offsetY, - pageX: e.pageX, - pageY: e.pageY, - screenX: e.screenX, - screenY: e.screenY, - shiftKey: e.shiftKey, - // timeStamp: e.timeStamp, - which: e.which, - x: e.x, - y: e.y, - }; - for (let i = 0; i < windows.length; i++) { - windows[i].emit(type, event); - } - }); -}); window.addEventListener('resize', e => { xrState.metrics[0] = window.innerWidth; xrState.metrics[1] = window.innerHeight; @@ -230,83 +184,76 @@ window.addEventListener('resize', e => { }); window.document.addEventListener('pointerlockchange', e => { const pointerLockElement = !!window.document.pointerLockElement; - for (let i = 0; i < windows.length; i++) { windows[i].emit('pointerlockchange', { pointerLockElement, }); } }); -window.addEventListener('drop', e => { - console.log('drop event', e); - /* const _readFiles = paths => { - const result = []; - - return Promise.all(paths.map(p => - new Promise((accept, reject) => { - fs.lstat(p, (err, stats) => { - if (!err) { - if (stats.isFile()) { - fs.readFile(p, (err, data) => { - if (!err) { - const file = new window.Blob([data]); - file.name = path.basename(p); - file.path = p; - result.push(file); - - accept(); - } else { - reject(err); - } - }); - } else if (stats.isDirectory()) { - fs.readdir(p, (err, fileNames) => { - if (!err) { - _readFiles(fileNames.map(fileName => path.join(p, fileName))) - .then(files => { - result.push.apply(result, files); - - accept(); - }) - .catch(err => { - reject(err); - }); - } else { - reject(err); - } - }); - } else { - accept(); - } - } else { - reject(err); - } - }); - }) - )) - .then(() => result); - }; +window.document.addEventListener('drop', e => { + const { + clientX, + clientY, + pageX, + pageY, + offsetX, + offsetY, + screenX, + screenY, + movementX, + movementY, + ctrlKey, + shiftKey, + altKey, + metaKey, + button, + dataTransfer, + } = e; - _readFiles(data.paths) - .then(files => { - const dataTransfer = new window.DataTransfer({ - files, - }); - const e = new window.DragEvent('drop'); - e.dataTransfer = dataTransfer; - canvas.dispatchEvent(e); - }) - .catch(err => { - console.warn(err.stack); + for (let i = 0; i < windows.length; i++) { + windows[i].emit('drop', { + clientX, + clientY, + pageX, + pageY, + offsetX, + offsetY, + screenX, + screenY, + movementX, + movementY, + ctrlKey, + shiftKey, + altKey, + metaKey, + button, + dataTransfer, }); - break; */ + } }); window.addEventListener('contextmenu', e => { e.preventDefault(); }); +window.addEventListener('vrdisplayconnect', e => { + const {display} = e; + const eyeParameters = ['left', 'right'].map(eye => display.getEyeParameters(eye)); + const width = Math.max(eyeParameters[0].renderWidth, eyeParameters[1].renderWidth); + const height = Math.max(eyeParameters[0].renderHeight, eyeParameters[1].renderHeight); -const topVrPresentState = { - /* hmdType: null, + xrState.renderWidth[0] = width; + xrState.renderHeight[0] = height; + + for (let i = 0; i < windows.length; i++) { + const win = windows[i]; + if (win.canvas) { + win.canvas.width = width * 2; + win.canvas.height = height; + } + } +}); + +/* const topVrPresentState = { + hmdType: null, windowHandle: null, fbo: null, msFbo: 0, @@ -317,8 +264,8 @@ const topVrPresentState = { mesher: null, planeTracker: null, handTracker: null, - eyeTracker: null, */ -}; + eyeTracker: null, +}; */ const requests = []; const handleRequest = req => { @@ -483,7 +430,8 @@ core.animate = (timestamp, frame, referenceSpace) => { // console.log('got gamepads', gamepads); // debugger; const _loadGamepad = i => { - const inputSource = inputSources[i]; + const handedness = i === 0 ? 'left' : 'right'; + const inputSource = inputSources.find(inputSource => inputSource.handedness === handedness); const xrGamepad = xrState.gamepads[i]; let pose, gamepad; @@ -544,4 +492,35 @@ core.animate = (timestamp, frame, referenceSpace) => { }; core.setSession(null); -})(); +export default { + async register() { + await navigator.serviceWorker.register('/sw.js'); + + if (!navigator.serviceWorker.controller) { + await new Promise((accept, reject) => { + const _controllerchange = () => { + if (navigator.serviceWorker.controller) { + navigator.serviceWorker.removeEventListener('controllerchange', _controllerchange); + clearTimeout(timeout); + accept(); + } + }; + navigator.serviceWorker.addEventListener('controllerchange', _controllerchange); + const timeout = setTimeout(() => { + console.warn('ew timed out'); + debugger; + }, 10 * 1000); + }); + } + + console.log('got registration', window.registration); + + GlobalContext.loadPromise.resolve(); + }, + async unregister() { + const registrations = await navigator.serviceWorker.getRegistrations(); + for (let i = 0; i < registrations.length; i++) { + await registrations[i].unregister(); + } + }, +}; diff --git a/src/VR.js b/src/VR.js index 05a8a18..76897d4 100644 --- a/src/VR.js +++ b/src/VR.js @@ -456,18 +456,6 @@ class FakeXRDisplay { GlobalContext.xrState.fakeVrDisplayEnabled[0] = 0; } - /* get width() { - return GlobalContext.xrState.renderWidth[0]*2; - } - set width(width) { - GlobalContext.xrState.renderWidth[0] = width/2; - } - get height() { - return GlobalContext.xrState.renderHeight[0]; - } - set height(height) { - GlobalContext.xrState.renderHeight[0] = height; - } */ get width() { if (GlobalContext.xrState.stereo[0]) { return GlobalContext.xrState.renderWidth[0]; diff --git a/src/Window.js b/src/Window.js index 6fb9a03..466c7c3 100644 --- a/src/Window.js +++ b/src/Window.js @@ -362,6 +362,7 @@ const _fetchText = src => fetch(src) window.XRPose = XR.XRPose; window.XRViewerPose = XR.XRViewerPose; window.XRInputSource = XR.XRInputSource; + window.DOMPoint = XR.DOMPoint; window.XRRay = XR.XRRay; window.XRInputPose = XR.XRInputPose; window.XRInputSourceEvent = XR.XRInputSourceEvent; @@ -844,7 +845,7 @@ self.onrunasync = req => { self.tickAnimationFrame(req); break; } - case 'enterXr': { + /* case 'enterXr': { console.log('handle enter xr', GlobalContext.id); self.vrdisplaypresentchange(); for (let i = 0; i < windows.length; i++) { @@ -862,7 +863,7 @@ self.onrunasync = req => { }); } break; - } + } */ case 'response': { const {keypath} = req; diff --git a/src/WindowBase.js b/src/WindowBase.js index 426133b..fc5ea8b 100644 --- a/src/WindowBase.js +++ b/src/WindowBase.js @@ -230,6 +230,11 @@ const _oninitmessage = async e => { target = 'input'; break; } + case 'drop': { + constructor = DragEvent; + target = 'document'; + break; + } default: { constructor = function(type) { return new Event(type, { diff --git a/src/XR.js b/src/XR.js index a9c7408..8a7beb1 100644 --- a/src/XR.js +++ b/src/XR.js @@ -103,7 +103,7 @@ class XRSession extends EventTarget { /* requestFrameOfReference() { // non-standard return this.requestReferenceSpace.apply(this, arguments); } */ - getInputSources() { + get inputSources() { return this._inputSources.filter(inputSource => inputSource.connected); } requestAnimationFrame(fn) { @@ -151,7 +151,7 @@ class XRSession extends EventTarget { this.dispatchEvent(new CustomEvent('end')); } update() { - const inputSources = this.getInputSources(); + const {inputSources} = this; const gamepads = GlobalContext.getGamepads(); for (let i = 0; i < inputSources.length; i++) { @@ -472,9 +472,14 @@ class XRInputSource { this._xrStateGamepad = xrStateGamepad; this.targetRaySpace = new XRSpace(); + this.targetRaySpace._pose.transform.position._buffer = xrStateGamepad.position; + this.targetRaySpace._pose.transform.orientation._buffer = xrStateGamepad.orientation; this.targetRaySpace._pose._realViewMatrix = xrStateGamepad.transformMatrix; this.targetRaySpace._pose._localViewMatrix = this.targetRaySpace._pose.transform.inverse.matrix; - this.gripSpace = new XRSpace(); + + this.gripSpace = new XRSpace(); // XXX make separate + this.gripSpace._pose.transform.position._buffer = xrStateGamepad.position; + this.gripSpace._pose.transform.orientation._buffer = xrStateGamepad.orientation; this.gripSpace._pose._realViewMatrix = xrStateGamepad.transformMatrix; this.gripSpace._pose._localViewMatrix = this.targetRaySpace._pose.transform.inverse.matrix; @@ -492,6 +497,39 @@ class XRInputSource { } } +class DOMPoint { + constructor(x, y, z, w) { + if (typeof x === 'object') { + this._buffer = x; + } else { + if (x === undefined) { + x = 0; + } + if (y === undefined) { + y = 0; + } + if (z === undefined) { + z = 0; + } + if (w === undefined) { + w = 1; + } + this._buffer = Float32Array.from([x, y, z, w]); + } + } + get x() { return this._buffer[0]; } + set x(x) { this._buffer[0] = x; } + get y() { return this._buffer[1]; } + set y(y) { this._buffer[1] = y; } + get z() { return this._buffer[2]; } + set z(z) { this._buffer[2] = z; } + get w() { return this._buffer[3]; } + set w(w) { this._buffer[3] = w; } + fromPoint(p) { + return new DOMPoint(p.x, p.y, p.z, p.w); + } +} + class XRRay { // non-standard constructor() { this.origin = new DOMPoint(); @@ -544,30 +582,30 @@ class XRRigidTransform extends EventTarget { scale = {x: 1, y: 1, z: 1}; } - this._position[0] = position.x; - this._position[1] = position.y; - this._position[2] = position.z; + this._position._buffer[0] = position.x; + this._position._buffer[1] = position.y; + this._position._buffer[2] = position.z; - this._orientation[0] = orientation.x; - this._orientation[1] = orientation.y; - this._orientation[2] = orientation.z; - this._orientation[3] = orientation.w; + this._orientation._buffer[0] = orientation.x; + this._orientation._buffer[1] = orientation.y; + this._orientation._buffer[2] = orientation.z; + this._orientation._buffer[3] = orientation.w; - this._scale[0] = scale.x; - this._scale[1] = scale.y; - this._scale[2] = scale.z; + this._scale._buffer[0] = scale.x; + this._scale._buffer[1] = scale.y; + this._scale._buffer[2] = scale.z; localMatrix - .compose(localVector.fromArray(this._position), localQuaternion.fromArray(this._orientation), localVector2.fromArray(this._scale)) + .compose(localVector.fromArray(this._position._buffer), localQuaternion.fromArray(this._orientation._buffer), localVector2.fromArray(this._scale._buffer)) .toArray(this.matrix); localMatrix .getInverse(localMatrix) .toArray(this.matrixInverse); localMatrix .decompose(localVector, localQuaternion, localVector2); - localVector.toArray(this._positionInverse); - localQuaternion.toArray(this._orientationInverse); - localVector2.toArray(this._scaleInverse); + localVector.toArray(this._positionInverse._buffer); + localQuaternion.toArray(this._orientationInverse._buffer); + localVector2.toArray(this._scaleInverse._buffer); } if (!this._inverse) { @@ -582,13 +620,13 @@ class XRRigidTransform extends EventTarget { { let index = this._inverse ? ((3 + 4 + 3 + 16) * Float32Array.BYTES_PER_ELEMENT) : 0; - this._position = new Float32Array(this._buffer, index, 3); + this._position = new DOMPoint(new Float32Array(this._buffer, index, 3)); index += 3 * Float32Array.BYTES_PER_ELEMENT; - this._orientation = new Float32Array(this._buffer, index, 4); + this._orientation = new DOMPoint(new Float32Array(this._buffer, index, 4)); index += 4 * Float32Array.BYTES_PER_ELEMENT; - this._scale = new Float32Array(this._buffer, index, 3); + this._scale = new DOMPoint(new Float32Array(this._buffer, index, 3)); index += 3 * Float32Array.BYTES_PER_ELEMENT; this.matrix = new Float32Array(this._buffer, index, 16); @@ -597,13 +635,13 @@ class XRRigidTransform extends EventTarget { { let index = this._inverse ? 0 : ((3 + 4 + 3 + 16) * Float32Array.BYTES_PER_ELEMENT); - this._positionInverse = new Float32Array(this._buffer, index, 3); + this._positionInverse = new DOMPoint(new Float32Array(this._buffer, index, 3)); index += 3 * Float32Array.BYTES_PER_ELEMENT; - this._orientationInverse = new Float32Array(this._buffer, index, 4); + this._orientationInverse = new DOMPoint(new Float32Array(this._buffer, index, 4)); index += 4 * Float32Array.BYTES_PER_ELEMENT; - this._scaleInverse = new Float32Array(this._buffer, index, 3); + this._scaleInverse = new DOMPoint(new Float32Array(this._buffer, index, 3)); index += 3 * Float32Array.BYTES_PER_ELEMENT; this.matrixInverse = new Float32Array(this._buffer, index, 16); @@ -653,9 +691,9 @@ class XRRigidTransform extends EventTarget { pushUpdate() { localMatrix .compose( - localVector.fromArray(this._position), - localQuaternion.fromArray(this._orientation), - localVector2.fromArray(this._scale) + localVector.fromArray(this._position._buffer), + localQuaternion.fromArray(this._orientation._buffer), + localVector2.fromArray(this._scale._buffer) ) .toArray(this.matrix); localMatrix @@ -663,9 +701,9 @@ class XRRigidTransform extends EventTarget { .toArray(this.matrixInverse); localMatrix .decompose(localVector, localQuaternion, localVector2); - localVector.toArray(this._positionInverse); - localQuaternion.toArray(this._orientationInverse); - localVector2.toArray(this._scaleInverse); + localVector.toArray(this._positionInverse._buffer); + localQuaternion.toArray(this._orientationInverse._buffer); + localVector2.toArray(this._scaleInverse._buffer); GlobalContext.xrState.offsetEpoch[0]++; } @@ -696,10 +734,10 @@ class XRBoundedReferenceSpace extends XRReferenceSpace { super(); this.boundsGeometry = [ - new GlobalContext.DOMPoint(-3, -3), - new GlobalContext.DOMPoint(3, -3), - new GlobalContext.DOMPoint(3, 3), - new GlobalContext.DOMPoint(-3, 3), + new DOMPoint(-3, -3), + new DOMPoint(3, -3), + new DOMPoint(3, 3), + new DOMPoint(-3, 3), ]; this.emulatedHeight = 0; } @@ -717,6 +755,7 @@ export { XRPose, XRViewerPose, XRInputSource, + DOMPoint, XRRay, XRInputPose, XRInputSourceEvent, diff --git a/src/xr-engine.js b/src/xr-engine.js index 955955e..083cabf 100644 --- a/src/xr-engine.js +++ b/src/xr-engine.js @@ -78,16 +78,50 @@ const XREngineProto = { win.canvas.height = GlobalContext.xrState.renderHeight[0]; win.canvas.style.width = '100%'; win.canvas.style.height = '100%'; - win.canvas.addEventListener('mousedown', e => { - e.preventDefault(); + ['mousedown', 'mouseup', 'click', 'dblclick', 'mousemove', 'wheel'].forEach(type => { + win.canvas.addEventListener(type, e => { + const event = { + altKey: e.altKey, + button: e.button, + buttons: e.buttons, + clientX: e.clientX, + clientY: e.clientY, + ctrlKey: e.ctrlKey, + deltaMode: e.deltaMode, + deltaX: e.deltaX, + deltaY: e.deltaY, + deltaZ: e.deltaZ, + // detail: e.detail, + layerX: e.layerX, + layerY: e.layerY, + metaKey: e.metaKey, + movementX: e.movementX, + movementY: e.movementY, + offsetX: e.offsetX, + offsetY: e.offsetY, + pageX: e.pageX, + pageY: e.pageY, + screenX: e.screenX, + screenY: e.screenY, + shiftKey: e.shiftKey, + // timeStamp: e.timeStamp, + which: e.which, + x: e.x, + y: e.y, + }; + for (let i = 0; i < GlobalContext.windows.length; i++) { + GlobalContext.windows[i].emit(type, event); + } + }); }); - win.canvas.addEventListener('mouseenter', e => { + const _mouseenter = () => { const {x, y, width, height} = win.canvas.getBoundingClientRect(); GlobalContext.xrState.canvasViewport[0] = x; GlobalContext.xrState.canvasViewport[1] = y; GlobalContext.xrState.canvasViewport[2] = width; GlobalContext.xrState.canvasViewport[3] = height; - }); + }; + win.canvas.addEventListener('mouseenter', _mouseenter); win.ctx = win.canvas.getContext(window.WebGL2RenderingContext ? 'webgl2' : 'webgl', { antialias: true, alpha: true, @@ -118,6 +152,8 @@ const XREngineProto = { this.dispatchEvent(new MessageEvent('canvas', { data: win.canvas, })); + + _mouseenter(); } return win.ctx; }; @@ -181,6 +217,7 @@ const XREngineProto = { } if (referenceSpaceType !== lastReferenceSpaceType) { + core.setReferenceSpace(referenceSpace); console.log(`referenceSpace changed to ${referenceSpaceType}`); } }; @@ -211,9 +248,9 @@ const XREngineProto = { win.canvas.width = fullWidth; win.canvas.height = height; - await win.runAsync({ + /* await win.runAsync({ method: 'enterXr', - }); + }); */ console.log('XR setup complete'); }); diff --git a/src/xr-iframe.js b/src/xr-iframe.js index 158e40a..dbb19a2 100644 --- a/src/xr-iframe.js +++ b/src/xr-iframe.js @@ -42,15 +42,28 @@ class XRIFrame extends HTMLElement { this.contentWindow = null; this.xrOffset = new XRRigidTransform(); + this.connected = false; this._highlight = null; this._extents = []; this._loadDistance = Infinity; this._data = {}; } + connectedCallback() { + this.connected = true; + + const {observedAttributes} = XRIFrame; + for (let i = 0; i < observedAttributes.length; i++) { + const attributeName = observedAttributes[i]; + this.attributeChangedCallback(attributeName, null, this.getAttribute(attributeName)); + } + } + disconnectedCallback() { + this.connected = false; + } async attributeChangedCallback(name, oldValue, newValue) { - await GlobalContext.loadPromise; + if (this.connected && newValue !== oldValue) { + await GlobalContext.loadPromise; - if (newValue !== oldValue) { if (name === 'src') { let url = this.getAttribute('src'); @@ -115,15 +128,15 @@ class XRIFrame extends HTMLElement { const {key, value} = event; if (key === 'position') { this.position = value; - this.xrOffset._position.set(value); + this.xrOffset._position._buffer.set(value); this.xrOffset.pushUpdate(); } else if (key === 'orientation') { this.orientation = value; - this.xrOffset._orientation.set(value); + this.xrOffset._orientation._buffer.set(value); this.xrOffset.pushUpdate(); } else if (key === 'scale') { this.scale = value; - this.xrOffset._scale.set(value); + this.xrOffset._scale._buffer.set(value); this.xrOffset.pushUpdate(); } }, @@ -153,7 +166,7 @@ class XRIFrame extends HTMLElement { if (position.length === 3) { position = position.map(s => parseFloat(s)); if (position.every(n => isFinite(n))) { - this.xrOffset._position.set(position); + this.xrOffset._position._buffer.set(position); this.xrOffset.pushUpdate(); } } @@ -162,7 +175,7 @@ class XRIFrame extends HTMLElement { if (orientation.length === 4) { orientation = orientation.map(s => parseFloat(s)); if (orientation.every(n => isFinite(n))) { - this.xrOffset._orientation.set(orientation); + this.xrOffset._orientation._buffer.set(orientation); this.xrOffset.pushUpdate(); } } @@ -171,7 +184,7 @@ class XRIFrame extends HTMLElement { if (scale.length === 3) { scale = scale.map(s => parseFloat(s)); if (scale.every(n => isFinite(n))) { - this.xrOffset._scale.set(scale); + this.xrOffset._scale._buffer.set(scale); this.xrOffset.pushUpdate(); } } diff --git a/sw.js b/sw.js index 19f5b10..6578eda 100644 --- a/sw.js +++ b/sw.js @@ -123,7 +123,7 @@ const _rewriteResExt = (url, originalUrl, headers, res) => { return _rewriteResText(res, jsString => { const result = jsString .replace(/https:\/\/www\.cryptovoxels\.com\//g, '/') - .replace('n._attached&&n.getEngine().enableVR()', 'n.getEngine().enableVR()') + .replace('r.enterVR()}))', 'r.enterVR()})),setTimeout(() => {this._btnVR.click();})') .replace(/this\._rigCameras\[0\]\.viewport=new ([a-zA-Z0-9\.]+)\(0\,0\,\.5\,1\)/g, 'this._rigCameras[0].viewport=new $1(0,0,new FakeXRDisplay().stereo?0.5:1,1)') .replace(/this\._rigCameras\[1\]\.viewport=new ([a-zA-Z0-9\.]+)\(\.5\,0\,\.5\,1\)/g, 'this._rigCameras[1].viewport=new $1(new FakeXRDisplay().stereo?0.5:0,0,new FakeXRDisplay().stereo?0.5:0,1)') return result;