Skip to content

Commit

Permalink
Introduce concept of deferred session requests
Browse files Browse the repository at this point in the history
Also merged in some navigation concepts from #247
  • Loading branch information
toji committed Jul 11, 2017
1 parent 9079886 commit f5f3452
Showing 1 changed file with 40 additions and 34 deletions.
74 changes: 40 additions & 34 deletions explainer.md
Original file line number Diff line number Diff line change
Expand Up @@ -478,60 +478,59 @@ function onDrawFrame() {
}
```
### Presenting automatically when the user interacts with the headset
### Responding to a reset pose
Most VR systems have a mechanism for allowing the user to reset which direction is "forward." For security and comfort reasons the WebVR API has no mechanism to trigger a pose reset programatically, but it can still be useful to know when it happens. Pages may want to take advantage of the visual discontinuity to reposition the user or other elements in the scene into a more natural position for the new orientation. Pages may also want to use the opportunity to clear or reset any additional transforms that have been applied if no longer needed.
Many VR devices have some way of detecting when the user has put the headset on or is otherwise trying to use the hardware. For example: an Oculus Rift or Vive have proximity sensors that indicate when the headset is being worn. And a Daydream device uses NFC tags to inform the phone when it's been placed in a headset. In both of these cases the user is showing a clear intent to begin using VR. A well behaved WebVR application should ideally begin presenting automatically in these scenarios. The `activate` event fired from the `VRDevice` can be used to accomplish that.
A page can be notified when a pose reset happens by listening for the 'resetpose' event from the 'VRSession'.
```js
vrDevice.addEventListener('activate', vrDeviceEvent => {
// The activate event acts as a user gesture, so exclusive access can be
// requested in the even handler.
vrDevice.requestSession().then(OnSessionStarted);
vrSession.addEventListener('resetpose', vrSessionEvent => {
// For an app that allows artificial Yaw rotation, this would be a perfect
// time to reset that.
ResetYawTransform();
});
```
Similarly, the `deactivate` event can be used to detect when the user removes the headset, at which point the application may want to end the session.
### Presenting automatically when the user interacts with the headset
Many VR devices have some way of detecting when the user has put the headset on or is otherwise trying to use the hardware. For example: an Oculus Rift or Vive have proximity sensors that indicate when the headset is being worn. And a Daydream device uses NFC tags to inform the phone when it's been placed in a headset. This is referred to as the `VRDevice` being "activated", and represents the user showing a clear intent to begin using VR. A well behaved WebVR application should ideally begin presenting automatically in these scenarios.
In order to start presenting when the `VRDevice` is activated pages can request a deferred session. A deferred session is requested using the normal `requestSession` function and given a `deferTill` option to indicate the criteria that must be fulfilled before the session will be created. So if a session is requested with the option `{ deferTill: 'activate' }` the request will remain outstanding until the `VRDevice` is activated, at which point the promise will resolve with the requested session (or reject, if necessary.)
Deferred sessions must be exclusive and will not be fulfilled if there is already an exclusive session active for the VR hardware device when the activation action occurs. Deferred requests do not need to be made within a user gesture.
```js
vrDevice.addEventListener('deactivate', vrDeviceEvent => {
if (vrSession) {
vrSession.endSession().then(OnSessionEnded);
}
});
// Requests that a session be created when the device is activated.
vrDevice.requestSession({ deferTill: 'activate' }).then(OnSessionStarted);
```
### Responding to a reset pose
Most VR systems have a mechanism for allowing the user to reset which direction is "forward." For security and comfort reasons the WebVR API has no mechanism to trigger a pose reset programatically, but it can still be useful to know when it happens. Pages may want to take advantage of the visual discontinuity to reposition the user or other elements in the scene into a more natural position for the new orientation. Pages may also want to use the opportunity to clear or reset any additional transforms that have been applied if no longer needed.
Once the session has ended a new deferred session request will need to be issued if the page wishes to respond to future activation actions.
A page can be notified when a pose reset happens by listening for the 'resetpose' event from the 'VRSession'.
To detect when the user removes the headset, at which point the page may want to end the session, listen for the `deactivate` event.
```js
vrSession.addEventListener('resetpose', vrSessionEvent => {
// For an app that allows artificial Yaw rotation, this would be a perfect
// time to reset that.
ResetYawTransform();
vrDevice.addEventListener('deactivate', (vrDeviceEvent) => {
if (vrSession) {
vrSession.endSession().then(OnSessionEnded);
}
});
```
### Page navigation
WebVR applications can, like any web page, link to other pages. In the context of a VR scene this is handled by setting `window.location` to the desired URL when the user performs some action. If the page being linked to is not VR-capable the user will either have to remove the VR device to view it (which the UA should explicitly instruct them to do) or the page could be shown as a 2D page in a VR browser.
WebVR applications can, like any web page, link to other pages. In the context of an exclusive `VRSession` this is handled by setting `window.location` to the desired URL when the user performs some action. If the page being linked to is not VR-capable the user will either have to remove the VR device to view it (which the UA should explicitly instruct them to do) or the page could be shown as a 2D page in a VR browser.
If the page being navigated to is VR capable, however, it's frequently desirable to allow the user to immediately begin using a VR session for that page as well, so that the user feels as though they are navigating through a single continuous VR experience. To achieve this the page can handle the `navigate` event, fired on the `navigator.vr` object. This event provides a `VRSession` for the `VRDevice` that the previous page was presenting to.
If the page being navigated to is VR capable, however, it's frequently desirable to allow the user to immediately create an exclusive `VRSession` for that page as well, so that the user feels as though they are navigating through a single continuous VR experience. To achieve this the page can query `navigator.vr.getNavigationDevice()` which returns a promise. If the page was navigated to from an exclusive `VRSession` the promise will resolve with the `VRDevice` that the previous session was created with. If not, the promise immediately rejects.
The `VRDevice` and `VRSession` must not retain any state set by the previous page, and need not make any guarantees about consistency of pose data between pages. They should maintain the same general implementation between pages for basic usage consistency. For example: The user agent should not switch users from the Oculus SDK to OpenVR between pages, or from Daydream to Cardboard, even though in both cases the users device could technically be used with either.
To indicate to indicate that you wish to continue presenting VR content on this page the handler must call `event.preventDefault()`.
Additionally the page can request a deferred session with the same device returned by `getNavigationDevice()` using the `{ deferTill: "navigate" }` option. If the page has been navigated to from an exclusive WebVR session, the request will be resolved when the page is ready to begin presenting. If the page was not navigated to from an exclusive session the request will reject immediately. This type of deferred session is only allowed once, and subsequent requests using the "navigate" defer criteria will also be rejected immediately. Additionally, the session must be requested within a User Agent-defined period after the page load or the ability to request a navigation deferred session expires.
```js
navigator.vr.addEventListener('navigate', vrSessionEvent => {
vrSessionEvent.preventDefault();
vrSession = vrSessionEvent.session;
vrDevice = vrSession.device;

// Ensure content is loaded and begin drawing.
onDrawFrame();
// Check to see if the page was navigated to from an exclusive session.
navigator.vr.getNavigationDevice().then(device => {
// If so request a deferred navigation session to provide a more continuous VR
// experience to the user.
device.requestSession({ deferTill: 'navigate' }).then(OnSessionStarted);
});
```
Expand Down Expand Up @@ -577,9 +576,9 @@ partial interface Navigator {
interface VR : EventTarget {
attribute EventHandler ondeviceconnect;
attribute EventHandler ondevicedisconnect;
attribute EventHandler onnavigate;

Promise<sequence<VRDevice>> getDevices();
Promise<VRDevice> getNavigationDevice();
};

//
Expand All @@ -593,7 +592,6 @@ interface VRDevice : EventTarget {
readonly attribute DOMString deviceName;
readonly attribute boolean isExternal;

attribute EventHandler onactivate;
attribute EventHandler ondeactivate;

Promise<boolean> supportsSession(optional VRSessionCreateParametersInit parameters);
Expand All @@ -604,12 +602,20 @@ interface VRDevice : EventTarget {
// Session
//

enum VRSessionDeferCriteria {
"",
"activate",
"navigate",
};

dictionary VRSessionCreateParametersInit {
boolean exclusive = true;
VRSessionDeferCriteria deferTill = "";
};

interface VRSessionCreateParameters {
readonly attribute boolean exclusive;
readonly attribute VRSessionDeferCriteria deferTill;
};

interface VRSession : EventTarget {
Expand Down

0 comments on commit f5f3452

Please sign in to comment.