UNPKG

@react-three/xr

Version:

VR/AR for react-three-fiber

94 lines (93 loc) 3.96 kB
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import { CombinedPointer } from '@pmndrs/pointer-events'; import { setupSyncIsVisible } from '@pmndrs/xr'; import { createXRStore as createXRStoreImpl, } from '@pmndrs/xr/internals'; import { useFrame, useStore as useRootStore, useThree } from '@react-three/fiber'; import { useContext, useEffect, useMemo } from 'react'; import { useStore } from 'zustand'; import { combinedPointerContext, xrContext } from './contexts.js'; import { XRElements } from './elements.js'; /** * Starting point for each XR application. * Allows to configure the session's features and defaults such as what controllers are rendered and how they can interact with the scene * @returns A new XR store */ export function createXRStore(options) { return createXRStoreImpl(options); } /** * Core XR component for connecting the `XRStore` with the scene. * Requires an `XRStore` which it will provide to its children. * * @param props * #### `children` - Children to be rendered inside the context. * #### `store` - The `XRStore` to be used for the session. */ export function XR({ children, store }) { store.setWebXRManager(useThree((s) => s.gl.xr)); const rootStore = useRootStore(); useEffect(() => { let initialCamera; return store.subscribe((state, prevState) => { if (state.session === prevState.session) { return; } //session has changed if (state.session != null) { const { camera, gl } = rootStore.getState(); initialCamera = camera; rootStore.setState({ camera: gl.xr.getCamera() }); return; } if (initialCamera == null) { //we always were in xr? return; } rootStore.setState({ camera: initialCamera }); }); }, [rootStore, store]); useFrame((state, _delta, frame) => store.onBeforeFrame(state.scene, state.camera, frame), -1000); useFrame(() => store.onBeforeRender()); return (_jsx(xrContext.Provider, { value: store, children: _jsxs(RootCombinedPointer, { children: [_jsx(XRElements, {}), children] }) })); } /** * Component for hiding the xr context to all child components. Can be used to create virtual displays and similar allowing the components inside the display to think they are not inside an XR environment, making them behave like when outside XR. * * @param props * @param props.children Children to be rendered inside the context. */ export function NotInXR({ children }) { const emptyStore = useMemo(() => createXRStore(), []); return _jsx(xrContext.Provider, { value: emptyStore, children: children }); } export function RootCombinedPointer({ children }) { const store = useXRStore(); const pointer = useMemo(() => new CombinedPointer(true), []); useEffect(() => setupSyncIsVisible(store, (visible) => pointer.setEnabled(visible, { timeStamp: performance.now() })), [store, pointer]); useFrame((state) => pointer.move(state.scene, { timeStamp: performance.now() }), -50); return _jsx(combinedPointerContext.Provider, { value: pointer, children: children }); } /** * Hook for getting the xr store from the context */ export function useXRStore() { const store = useContext(xrContext); if (store == null) { throw new Error(`XR features can only be used inside the <XR> component`); } return store; } /** * Returns the XR store object from a parent {@link XR} component. If no component is found `undefined` is returned. * You most likely should be using {@link useXRStore} instead. */ export function UNSAFE_useXRStore() { const store = useContext(xrContext); return store; } /** * Hook for reading the state from the xr store */ export function useXR(selector = (state) => state, equalityFn) { return useStore(useXRStore(), selector, equalityFn); }