Skip to content

add: requests immersive, clicking Enter VR launches inline #171

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Aug 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 47 additions & 49 deletions src/p5xr/core/p5xr.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export default class p5xr {
this.xrDevice = null;
this.xrButton = null;
this.isVR = null;
this.hasImmersive = null;
this.xrSession = null;
this.xrRefSpace = null;
this.xrViewerSpace = null;
Expand Down Expand Up @@ -93,56 +94,53 @@ export default class p5xr {
this.xrButton = new p5xrButton({
onRequestSession: this.onXRButtonClicked.bind(this),
onEndSession: this.onSessionEnded.bind(this),
textEnterXRTitle: this.isVR ? 'ENTER VR' : 'ENTER AR',
textEnterXRTitle: 'LOADING',
});
let header = document.querySelector('header');
if (!header) {
header = document.createElement('header');
document.querySelector('body').appendChild(header);
}
header.appendChild(this.xrButton.domElement);
}

/**
* Disables button, only called when AR session is attempted and
* unsupported. VR has inline fallback.
*/
disableButton() {
this.xrButton.setTitle('AR Unavailable');
this.xrButton.setTooltip('No XR headset found.');
this.xrButton.__setDisabledAttribute(true);
this.sessionCheck();
}

/**
* Called by `createVRCanvas()` or `createARCanvas`.
* Checks what kind of session is supported by the device.
*/
sessionCheck() {
const msg = window.injectedPolyfill ? 'with polyfill' : 'without polyfill';

if (this.isVR) {
const msg = window.injectedPolyfill ? ' with polyfill' : ' without polyfill';
// WebXR availabilty
if(navigator.xr){
console.log('XR Available')
navigator.xr.isSessionSupported('immersive-vr').then((supported) => {
if (supported) {
console.log(`VR supported ${msg}`);
this.xrButton.setDevice(true);
this.isImmersive = true;
} else {
console.log('This device does not support immersive VR sessions.');
this.isImmersive = false;
}
this.xrButton.setDevice(true);
}).catch((e) => {
console.log(e.message);
});
} else {
navigator.xr.isSessionSupported('immersive-ar').then((supported) => {
if (supported) {
console.log(`AR supported ${msg}`);
this.xrButton.setDevice(true);
} else {
this.disableButton();
}
this.hasImmersive = supported;
});
if(this.isVR){
// Checks if VR is supported
this.xrButton.setTitle('Enter VR');
this.xrButton.setTooltip('Enter VR');
this.xrButton.enable();
console.log(`VR supported ${msg}`);
this.xrButton.setDevice(true);
} else {
// Checks if AR is supported
navigator.xr.isSessionSupported('immersive-ar').then((supported) => {
if (supported) {
this.xrButton.setTitle('Enter AR');
this.xrButton.setTooltip('Enter AR');
this.xrButton.enable();
console.log(`AR supported ${msg}`);
this.xrButton.setDevice(true);
} else {
this.xrButton.setTitle('AR Not Available');
this.xrButton.setTooltip('AR Not Available');
this.xrButton.disable();
console.log(`AR not supported`);
}
});
}
}
else {
console.log('XR Not Available')
this.disableButton()
}
}

Expand Down Expand Up @@ -280,24 +278,24 @@ export default class p5xr {
* Called either when the user has explicitly ended the session
* or when the UA has ended the session for any reason.
* The xrSession is ended and discarded. p5 is reset with `remove()`
*
* //TODO: Revisit how we exit session
*/
onSessionEnded() {
if (!this.isVR) {
this.xrHitTestSource.cancel();
this.xrHitTestSource = null;
} else {
if(this.isImmersive){
console.log('Exiting immersive session')
this.isImmersive = false
this.sessionCheck()
console.log('Requesting new session')
navigator.xr.requestSession('inline').then(this.startSketch.bind(this));
}
}
if (this.xrSession) {
this.xrSession.end();
this.xrSession = null;
}
const p5Canvi = document.getElementsByClassName('p5Canvas');
while (p5Canvi.length > 0) {
p5Canvi[0].parentNode.removeChild(p5Canvi[0]);
if (this.isImmersive && this.hasImmersive) {
this.isImmersive = false
}
this.xrButton.session = null;
this.xrButton.setTitle(this.isVR ? 'ENTER VR' : 'ENTER AR');
this.gl = null;
}

printUnsupportedMessage() {
Expand Down
23 changes: 1 addition & 22 deletions src/p5xr/core/p5xrButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,6 @@ export default class p5xrButton {
*/
setDevice(device) {
this.device = device;
this.__updateButtonState();
return this;
}

Expand Down Expand Up @@ -383,7 +382,7 @@ export default class p5xrButton {
*/
__onXRButtonClick() {
if (this.session) {
this.options.onEndSession(this.session);
this.options.onRequestSession(this.device)
} else if (this.device) {
// feature detect
if (typeof DeviceMotionEvent !== 'undefined' && typeof DeviceMotionEvent.requestPermission === 'function') {
Expand All @@ -409,24 +408,4 @@ export default class p5xrButton {
this.options.onRequestSession(this.device);
}
}

/**
* Updates the display of the button based on it's current state
* @private
*/
__updateButtonState() {
if (this.session) {
this.setTitle(this.options.textExitXRTitle);
this.setTooltip('Exit XR presentation');
this.__setDisabledAttribute(false);
} else if (this.device) {
this.setTitle(this.options.textEnterXRTitle);
this.setTooltip('Enter XR');
this.__setDisabledAttribute(false);
} else {
this.setTitle(this.options.textXRNotFoundTitle);
this.setTooltip('No XR headset found.');
this.__setDisabledAttribute(true);
}
}
}
98 changes: 56 additions & 42 deletions src/p5xr/p5vr/p5vr.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ export default class p5vr extends p5xr {
constructor() {
super();
this.isVR = true;
this.isImmersive = true;

this.isImmersive = false;
this.lookYaw = 0;
this.lookPitch = 0;
this.LOOK_SPEED = 0.0025;
Expand All @@ -23,13 +22,11 @@ export default class p5vr extends p5xr {
this.primaryTouch = undefined;
this.prevTouchX = undefined;
this.prevTouchY = undefined;
navigator.xr.requestSession('inline').then(this.startSketch.bind(this));
}

initVR() {
this.createButton();
if (navigator.xr) {
this.sessionCheck();
}
this.createButton()
}

/**
Expand All @@ -40,7 +37,7 @@ export default class p5vr extends p5xr {
* @param {XRSession}
*/
startSketch(session) {
this.xrSession = this.xrButton.session = session;
this.xrSession = session;
this.canvas = p5.instance.canvas;
this.canvas.style.visibility = 'visible';

Expand All @@ -49,46 +46,74 @@ export default class p5vr extends p5xr {
window.setup();
p5.instance._millisStart = window.performance.now();
}
const refSpaceRequest = this.isImmersive ? 'local' : 'viewer';
this.xrSession.requestReferenceSpace(refSpaceRequest)
.then((refSpace) => {
this.xrRefSpace = refSpace;
// Inform the session that we're ready to begin drawing.
this.xrSession.requestAnimationFrame(this.onXRFrame.bind(this));
if (!this.isImmersive) {
this.xrSession.updateRenderState({
baseLayer: new XRWebGLLayer(this.xrSession, this.gl),
inlineVerticalFieldOfView: 90 * (Math.PI / 180),
});
this.addInlineViewListeners(this.canvas);
}
});
this.onRequestSession();
}
/**
* Helper function to reset XR and GL, should be called between
* ending an XR session and starting a new XR session
*
*/
resetXR(){
this.xrDevice = null;
this.xrSession = null;
this.xrRefSpace = null;
this.xrViewerSpace = null;
this.xrHitTestSource = null;
this.gl = null;
this.frame = null;
}

/**
* `device.requestSession()` must be called within a user gesture event.
* `navigator.xr.requestSession('immersive-vr')` must be called within a user gesture event.
* @param {XRDevice}
*/
onXRButtonClicked() {
if (this.isImmersive) {
console.log('requesting session with mode: immersive-vr');
if (this.hasImmersive) {
console.log('Requesting session with mode: immersive-vr');
this.isImmersive = true;
this.resetXR();
navigator.xr.requestSession('immersive-vr').then(this.startSketch.bind(this));
} else {
console.log('requesting session with mode: non-immersive-vr');
this.xrButton.hide();
// Start up an inline session, which should always be supported on
// browsers that support WebXR regardless of the available hardware.
navigator.xr.requestSession('inline').then(this.startSketch.bind(this));
//TODO: Request Fullscreen
}
}

onRequestSession() {
this.xrButton.setTitle(this.isVR ? 'EXIT VR' : 'EXIT AR');
p5.instance._renderer._curCamera.cameraType = 'custom';
this.gl = this.canvas.getContext('webgl', { xrCompatible: true });

if (!this.isImmersive) {
this.setupBaseLayer();
this.setupReferenceSpace();
this.xrSession.updateRenderState({
inlineVerticalFieldOfView: 70 * (Math.PI / 180),
});
this.addInlineViewListeners(this.canvas);
} else {
this.gl.makeXRCompatible().then(() => {
this.setupBaseLayer();
this.setupReferenceSpace();
}).catch((e) => {
console.log(e);
const refSpaceRequest = this.isImmersive ? 'local' : 'viewer';

this.gl = this.canvas.getContext('webgl');
this.gl.makeXRCompatible().then(() => {
// Get a frame of reference, which is required for querying poses.
// 'local' places the initial pose relative to initial location of viewer
// 'viewer' is only for inline experiences and only allows rotation
this.xrSession.requestReferenceSpace(refSpaceRequest)
.then((refSpace) => {
this.xrRefSpace = refSpace;
});
}

// Use the p5's 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;
this.xrSession.updateRenderState({ baseLayer: new XRWebGLLayer(this.xrSession, this.gl) });
}).catch((e) => {
console.log(e);
})

// Request initial animation frame
this.xrSession.requestAnimationFrame(this.onXRFrame.bind(this));
Expand All @@ -102,17 +127,6 @@ export default class p5vr extends p5xr {
this.xrSession.updateRenderState({ baseLayer: this.baseLayer });
}

setupReferenceSpace() {
// Get a frame of reference, which is required for querying poses.
// 'local' places the initial pose relative to initial location of viewer
// 'viewer' is only for inline experiences and only allows rotation
const refSpaceRequest = this.isImmersive ? 'local' : 'viewer';
this.xrSession.requestReferenceSpace(refSpaceRequest)
.then((refSpace) => {
this.xrRefSpace = refSpace;
// Inform the session that we're ready to begin drawing.
});
}

/**
* clears the background based on the current clear color (`curClearColor`)
Expand Down
6 changes: 0 additions & 6 deletions tests/manual-test-examples/p5vr/basic/example.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ function setup() {

function calculate() {
checkSync();
checkSetup();
counter++;
}

Expand All @@ -27,11 +26,6 @@ function draw() {

}

function checkSetup() {
if(setupCounter !== 2) {
console.error('setup() running incorrectly');
}
}

function checkSync() {
if(counter === 0) {return;}
Expand Down