immersive-web / webxr-polyfill

Use the WebXR Device API today, providing fallbacks to native WebVR 1.1 and Cardboard
Apache License 2.0
381 stars 84 forks source link

When should XRSession.inputSources be set up? #81

Closed takahirox closed 4 years ago

takahirox commented 5 years ago

Background

If XRSession.inputSources is accessed it calls device.getInputSources() where device is WebVRDevice.

  get inputSources() {
    return this[PRIVATE].device.getInputSources();
  }

https://github.com/immersive-web/webxr-polyfill/blob/master/src/api/XRSession.js#L314

WebVRDevice.getInputSources() returns an array composed from its .gamepadInputSources

  getInputSources() {
    let inputSources = [];
    for (let i in this.gamepadInputSources) {
      inputSources.push(this.gamepadInputSources[i].inputSource);
    }
    return inputSources;
  }

https://github.com/immersive-web/webxr-polyfill/blob/master/src/devices/WebVRDevice.js#L567

.gamepadInputSources is empty first, and they're set up in onFrameStart().

  onFrameStart(sessionId) {

          ...

          this.gamepadInputSources[i] = inputSourceImpl;

          ...

  }

https://github.com/immersive-web/webxr-polyfill/blob/master/src/devices/WebVRDevice.js#L295

I faced a problem in Three.js with this behavior. Three.js keeps session.inputSources and uses later here.

            inputSources = session.inputSources;

https://github.com/mrdoob/three.js/blob/r108/src/renderers/webvr/WebXRManager.js#L144

But the above line is called before the first immersive animation frame is invoked. So inputSources is an empty array even if some controllers exist.

Question

Is this behavior intentional? If so, should the problem be fixed in Three.js side?

The spec mentions

Each XRSession has a list of active XR input sources (a list of XRInputSource) which MUST be initially an empty list.

The inputSources attribute returns the XRSession's list of active XR input sources.

When new XR input sources become available for XRSession session, the user agent MUST run the following steps: ... Extend session’s list of active XR input sources with added. ...

https://immersive-web.github.io/webxr/#xrsession-interface

I'm not really sure exactly when they should be set up. (Sorry if I'm missing) In immersive animation frame? Or synchronously when XRSession is instanciated if controllers are already available?

jsantell commented 5 years ago

IIUC, the issue is input sources are not available until at least one animation frame elapses.

Not sure if this was intentional or not (ping @toji), but IIUC the spec correctly, it makes sense that the inputSources are not populated before those steps can be run (e.g. fire an event with a new array). It appears waiting a frame would work just as well, spec-wise.

That being said, I'd expect for three.js to respond to that event, and not rely on it being available at session creation (if in fact the polyfill is firing that event for the input sources after the first animation frame).

takahirox commented 5 years ago

IIUC, the issue is input sources are not available until at least one animation frame elapses.

Yes, correct. Code snippet

navigator.xr.requestSession('immersive-vr', sessionInit).then(session => {
  session.updateRenderState({baseLayer: new XRWebGLLayer(session, gl)});
  session.requestReferenceSpace(referenceSpaceType).then(onRequestReferenceSpace);
  // session.inputSources is an empty array here with the polyfill
  // even if some controllers(gamepads) are available.
  inputSources = session.inputSources;

  ....

});

And thanks for the comment. Just in case, I want to also hear the comment fom @toji and if it's a correct behavior I'll try to fix the problem in Three.js side.

takahirox commented 5 years ago

https://immersive-web.github.io/webxr/#xrsession-interface

When new XR input sources become available for XRSession session, the user agent MUST run the following steps:

  1. Let added be a new list.
  2. For each new XR input source:
    1. Let inputSource be a new XRInputSource.
    2. Add inputSource to added.
  3. Queue a task to perform the following steps:
    1. Extend session’s list of active XR input sources with added.
    2. Fire an XRInputSourcesChangeEvent named inputsourceschange on session with added set to added.

If I understand correctly, inputsourceschange event should be fired when inputSources are populated at the first animation frame, correct?

If the polyfill will do so, Three.js doesn't need to be fixed because it watches inputsourceschange event and update its inputSources when it's fired.

https://github.com/mrdoob/three.js/blob/r108/src/renderers/webvr/WebXRManager.js#L146-L158

Related: #37

takahirox commented 4 years ago

inputsourceschange event is implemented in the polyfill and the problem I faced should be solved. Closing this issue.