Skip to content
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

feat: create AR/VR buttons in react, bypass context for session init #142

Merged
merged 17 commits into from Jun 19, 2022

Conversation

CodyJasonBennett
Copy link
Member

@CodyJasonBennett CodyJasonBennett commented Jun 16, 2022

Continues #76

Refactors the internal XRButton and XRCanvas to exist in React and init session state outside of XR context, so buttons can exist outside of the canvas. This is fully backwards compatible with previous versions that utilize three's VRButton & ARButton.

type XRButtonStatus = 'unsupported' | 'exited' | 'entered'
interface XRButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
  /** The type of `XRSession` to create */
  mode: 'AR' | 'VR' | 'inline'
  /**
   * `XRSession` configuration options.
   * @see https://immersive-web.github.io/webxr/#feature-dependencies
   */
  sessionInit?: XRSessionInit
  /** Whether this button should only enter an `XRSession` */
  enterOnly?: boolean
  /** Whether this button should only exit an `XRSession` */
  exitOnly?: boolean
  /** React children, can also accept a callback returning an `XRButtonStatus` */
  children?: React.ReactNode | ((status: React.ReactNode) => React.ReactNode)
}

Additionally, XRButton and XRCanvas are now exported to enable more composition options. For example, this would be equivalent to VRCanvas:

<XRButton mode="VR" sessionInit={sessionInit} />
<XRCanvas>
  // ...
</XRCanvas>

Furthermore, session events are exposed as props, and reference space can be configured on the XRCanvas:

interface XRManagerEvent {
  type: 'sessionstart' | 'sessionend'
  target: WebXRManager
}
interface XRControllerEvent {
  type: XRControllerEventType
  data?: XRInputSource
}
interface XRCanvasEvent {
  readonly nativeEvent: XRManagerEvent | XRControllerEvent | XRSessionEvent
  readonly session: XRSession
}
interface XRProps {
  /**
   * Enables foveated rendering,
   * 0 = no foveation = full resolution,
   * 1 = maximum foveation = the edges render at lower resolution
   */
  foveation?: number
  /** Type of WebXR reference space to use. */
  referenceSpace?: XRReferenceSpaceType
  /** Called as an XRSession is requested. */
  onSessionStart?: (event: XRCanvasEvent) => void
  /** Called after an XRSession is terminated. */
  onSessionEnd?: (event: XRCanvasEvent) => void
  /** Called when an XRSession is hidden or unfocused. */
  onVisibilityChange?: (event: XRCanvasEvent) => void
  /** Called when available inputsources change. */
  onInputSourcesChange?: (event: XRCanvasEvent) => void
  children: React.ReactNode
}

@saitonakamura
Copy link
Sponsor Collaborator

Coming with a feedback about session configuration. We stopped using react-xr buttons a while ago and we're creating the session manually so here are the things we're currently doing with comments how it can relate to this pr

// this is pretty straightforward and sessionInit is supported
const session = await navigator.xr.requestSession(
  "immersive-vr",
  sessionInit
);

// end handler is also ok, we just want to know if session ends, any callback will suffice or we can just get session from the gl.xr
session.addEventListener("end", sessionEndHandler);
// this one is more specific but it's needed for more advanced vr scenarios, for instance we stop the video while session is blurred/hidden
session.addEventListener("visibilitychange", handlerVisChange);

const xrManager = getRenderer().xr;
// we set specific reference space we need
xrManager.setReferenceSpaceType("local-floor");
// and set it three, because we create a session outside of built-in buttons
await xrManager.setSession(session as unknown as THREE.XRSession);
// and we also set specific foveation
xrManager.setFoveation(0); // must be after  xrManager.setSession()

// but that's the main reason why we do it: we need access to the session outside of react-xr context
// our usecase are to render a ui outside of r3f and understand whether the session is there, it's state and to be able to exit it from here
// we also use a zustand store for it, but a userland one
setImmersiveSession(session);

@CodyJasonBennett
Copy link
Member Author

CodyJasonBennett commented Jun 16, 2022

Reference space and foveation would be good config options. Not sure about accessing the session outside of react-xr context. I have a global zustand store to pass around the XRSession since it's assumed that only one XRCanvas will be used at a time, but I'm not sure if/how that should be exposed at all.

@CodyJasonBennett
Copy link
Member Author

CodyJasonBennett commented Jun 17, 2022

I have a way to do the latter in #143, but that's v4-only. This PR would have to be merged first and picked to v3, or mirrored against the v3 branch.

@saitonakamura
Copy link
Sponsor Collaborator

but I'm not sure if/how that should be exposed at all.

Exactly the same thought

@saitonakamura
Copy link
Sponsor Collaborator

What do you think about onSessionStart , onSessionEnd, onVisibilityChange handlers on XRCanvas?

@CodyJasonBennett
Copy link
Member Author

CodyJasonBennett commented Jun 17, 2022

I'd want to also add inputsourceschange to the list, but that would be perfect regarding the session hand-off issue, although the active session should probably live in the XRContext/XRStore in v3/v4 respectively.

src/XR.tsx Outdated
Comment on lines 102 to 110
export type XRCanvasEventType = 'sessionstart' | 'sessionend' | 'visibilitychange' | 'inputsourceschange'
export class XRCanvasEvent extends Event {
readonly session: XRSession

constructor(type: XRCanvasEventType, session: XRSession) {
super(type)
this.session = session
}
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a little annoying. Three doesn't emulate XRSessionEvent in its WebXRManager and XREvents.tsx exports an interface called XREvent which would more appropriately be XRControllerEvent.

I think I'll rename these in v4 so XREvent becomes more generic and extends to XRControllerEvent. This should also maybe be an interface/object instead of an Event, as its methods aren't effectful.

@CodyJasonBennett CodyJasonBennett merged commit 4a7c968 into master Jun 19, 2022
@CodyJasonBennett CodyJasonBennett deleted the feat/button-component branch June 19, 2022 14:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants