@opsydyn/effect-three-foldkit 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +70 -0
- package/LICENSE +21 -0
- package/README.md +99 -2
- package/dist/assets.d.ts +53 -0
- package/dist/assets.d.ts.map +1 -0
- package/dist/dom/animationFrames.d.ts +12 -0
- package/dist/dom/animationFrames.d.ts.map +1 -0
- package/dist/dom/canvas.d.ts +26 -0
- package/dist/dom/canvas.d.ts.map +1 -0
- package/dist/dom/latch.d.ts +15 -0
- package/dist/dom/latch.d.ts.map +1 -0
- package/dist/dom/models.d.ts +10 -0
- package/dist/dom/models.d.ts.map +1 -0
- package/dist/dom/overlays.d.ts +24 -0
- package/dist/dom/overlays.d.ts.map +1 -0
- package/dist/dom/pointer.d.ts +49 -0
- package/dist/dom/pointer.d.ts.map +1 -0
- package/dist/dom/postprocessing.d.ts +22 -0
- package/dist/dom/postprocessing.d.ts.map +1 -0
- package/dist/dom/property.d.ts +15 -0
- package/dist/dom/property.d.ts.map +1 -0
- package/dist/dom/reconciled.d.ts +10 -0
- package/dist/dom/reconciled.d.ts.map +1 -0
- package/dist/dom/renderSettings.d.ts +58 -0
- package/dist/dom/renderSettings.d.ts.map +1 -0
- package/dist/dom/typedScenes.d.ts +14 -0
- package/dist/dom/typedScenes.d.ts.map +1 -0
- package/dist/dom/updates.d.ts +8 -0
- package/dist/dom/updates.d.ts.map +1 -0
- package/dist/dom/viewport.d.ts +40 -0
- package/dist/dom/viewport.d.ts.map +1 -0
- package/dist/dom/webgl.d.ts +12 -0
- package/dist/dom/webgl.d.ts.map +1 -0
- package/dist/dom/webgpu.d.ts +24 -0
- package/dist/dom/webgpu.d.ts.map +1 -0
- package/dist/dom/webgpuPostprocessing.d.ts +4 -0
- package/dist/dom/webgpuPostprocessing.d.ts.map +1 -0
- package/dist/dom/xr.d.ts +53 -0
- package/dist/dom/xr.d.ts.map +1 -0
- package/dist/dom/xrGlide.d.ts +49 -0
- package/dist/dom/xrGlide.d.ts.map +1 -0
- package/dist/dom/xrHub.d.ts +45 -0
- package/dist/dom/xrHub.d.ts.map +1 -0
- package/dist/dom/xrInputs.d.ts +40 -0
- package/dist/dom/xrInputs.d.ts.map +1 -0
- package/dist/dom/xrSpaces.d.ts +33 -0
- package/dist/dom/xrSpaces.d.ts.map +1 -0
- package/dist/dom/xrTarget.d.ts +36 -0
- package/dist/dom/xrTarget.d.ts.map +1 -0
- package/dist/element.d.ts +771 -0
- package/dist/element.d.ts.map +1 -0
- package/dist/environment.d.ts +64 -0
- package/dist/environment.d.ts.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2041 -0
- package/dist/index.js.map +38 -0
- package/dist/mount.d.ts +14 -0
- package/dist/mount.d.ts.map +1 -0
- package/dist/overlayView.d.ts +23 -0
- package/dist/overlayView.d.ts.map +1 -0
- package/dist/view.d.ts +179 -0
- package/dist/view.d.ts.map +1 -0
- package/dist/xr.d.ts +34 -0
- package/dist/xr.d.ts.map +1 -0
- package/package.json +72 -4
- package/src/assets.ts +151 -0
- package/src/dom/animationFrames.ts +32 -0
- package/src/dom/canvas.ts +54 -0
- package/src/dom/latch.ts +36 -0
- package/src/dom/models.ts +37 -0
- package/src/dom/overlays.ts +63 -0
- package/src/dom/pointer.ts +163 -0
- package/src/dom/postprocessing.ts +116 -0
- package/src/dom/property.ts +43 -0
- package/src/dom/reconciled.ts +51 -0
- package/src/dom/renderSettings.ts +117 -0
- package/src/dom/typedScenes.ts +21 -0
- package/src/dom/updates.ts +14 -0
- package/src/dom/viewport.ts +97 -0
- package/src/dom/webgl.ts +216 -0
- package/src/dom/webgpu.ts +205 -0
- package/src/dom/webgpuPostprocessing.ts +102 -0
- package/src/dom/xr.ts +211 -0
- package/src/dom/xrGlide.ts +114 -0
- package/src/dom/xrHub.ts +118 -0
- package/src/dom/xrInputs.ts +309 -0
- package/src/dom/xrSpaces.ts +287 -0
- package/src/dom/xrTarget.ts +71 -0
- package/src/element.ts +114 -0
- package/src/environment.ts +78 -0
- package/src/index.ts +71 -0
- package/src/mount.ts +305 -0
- package/src/overlayView.ts +71 -0
- package/src/view.ts +254 -0
- package/src/xr.ts +36 -0
package/src/view.ts
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The scene view: one call from a Foldkit `view` renders a scene and maps its events to
|
|
3
|
+
* the app's typed Messages. The scene is plain data derived from the Model.
|
|
4
|
+
*
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
7
|
+
import type { SceneDescription, SceneSchema } from '@opsydyn/effect-three';
|
|
8
|
+
import { Array as Arr, Option } from 'effect';
|
|
9
|
+
import { Mount } from 'foldkit';
|
|
10
|
+
import type { Html, HtmlBuilder } from 'foldkit/html';
|
|
11
|
+
|
|
12
|
+
import { markTyped } from './dom/typedScenes.ts';
|
|
13
|
+
import {
|
|
14
|
+
type ReconciledDetail,
|
|
15
|
+
type SceneError,
|
|
16
|
+
sceneElement,
|
|
17
|
+
type SceneLifecycle,
|
|
18
|
+
type XrSessionDetail,
|
|
19
|
+
} from './element.ts';
|
|
20
|
+
import type { SceneMount } from './mount.ts';
|
|
21
|
+
import { overlayLayer, type OverlayView, withOverlays } from './overlayView.ts';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Where the pointer met an interactive mesh: key, world-space point, distance from the
|
|
25
|
+
* camera, world-space face normal and texture coordinates.
|
|
26
|
+
*/
|
|
27
|
+
export type HitDetail = typeof SceneSchema.HitDetail.Type;
|
|
28
|
+
|
|
29
|
+
/** Detail of click, double-click, pointer-down and pointer-up events: the hit and button. */
|
|
30
|
+
export type ButtonDetail = typeof SceneSchema.ButtonDetail.Type;
|
|
31
|
+
|
|
32
|
+
/** Detail of wheel events: the hit and the wheel deltas. */
|
|
33
|
+
export type WheelDetail = typeof SceneSchema.WheelDetail.Type;
|
|
34
|
+
|
|
35
|
+
/** Detail of a click that hit no interactive mesh. */
|
|
36
|
+
export type MissDetail = typeof SceneSchema.MissDetail.Type;
|
|
37
|
+
|
|
38
|
+
/** Detail of drag events: the captured mesh, the button and the pointer's world-space ray. */
|
|
39
|
+
export type DragDetail = typeof SceneSchema.DragDetail.Type;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* A drag, wheel or pinch on the canvas, when the scene claims gestures. Pass it to
|
|
43
|
+
* `orbit` to move a camera kept in the Model.
|
|
44
|
+
*/
|
|
45
|
+
export type Gesture = typeof SceneSchema.Gesture.Type;
|
|
46
|
+
|
|
47
|
+
/** Detail of collision events: the two bodies' keys, and whether they began touching. */
|
|
48
|
+
export type CollisionDetail = typeof SceneSchema.CollisionDetail.Type;
|
|
49
|
+
|
|
50
|
+
/** Detail of contact force events: the two bodies, and how hard they pushed. */
|
|
51
|
+
export type ContactForceDetail = typeof SceneSchema.ContactForceDetail.Type;
|
|
52
|
+
|
|
53
|
+
/** Detail of measured events: a node's world bounding box. */
|
|
54
|
+
export type MeasuredDetail = typeof SceneSchema.MeasuredDetail.Type;
|
|
55
|
+
|
|
56
|
+
/** Detail of auto-rotated events: where a camera turning by itself got to. */
|
|
57
|
+
export type AutoRotatedDetail = typeof SceneSchema.AutoRotatedDetail.Type;
|
|
58
|
+
|
|
59
|
+
/** Detail of label measured events: a label's laid-out size in its own units. */
|
|
60
|
+
export type LabelMeasuredDetail = typeof SceneSchema.LabelMeasuredDetail.Type;
|
|
61
|
+
|
|
62
|
+
/** Detail of XR stick events: a thumbstick pushed past halfway in a direction, or let back. */
|
|
63
|
+
export type XrStickDetail = typeof SceneSchema.XrStickDetail.Type;
|
|
64
|
+
|
|
65
|
+
/** Detail of XR button events: a controller button pressed or released. */
|
|
66
|
+
export type XrButtonDetail = typeof SceneSchema.XrButtonDetail.Type;
|
|
67
|
+
|
|
68
|
+
/** Detail of XR moved events: where gliding left the player. */
|
|
69
|
+
export type XrMovedDetail = typeof SceneSchema.XrMovedDetail.Type;
|
|
70
|
+
|
|
71
|
+
/** Detail of XR planes events: every plane the session detected, as it is now. */
|
|
72
|
+
export type XrPlanesDetail = typeof SceneSchema.XrPlanesDetail.Type;
|
|
73
|
+
|
|
74
|
+
/** Detail of XR meshes events: every mesh the session detected, as it is now. */
|
|
75
|
+
export type XrMeshesDetail = typeof SceneSchema.XrMeshesDetail.Type;
|
|
76
|
+
|
|
77
|
+
/** Detail of XR surface events: in AR, where a select met a real surface, and its normal. */
|
|
78
|
+
export type XrSurfaceDetail = typeof SceneSchema.XrSurfaceDetail.Type;
|
|
79
|
+
|
|
80
|
+
/** Detail of performance changes: the measured fps and the new factor, 0 to 1. */
|
|
81
|
+
export type PerformanceDetail = typeof SceneSchema.PerformanceDetail.Type;
|
|
82
|
+
|
|
83
|
+
/** Detail of pointer-leave events: the key that is no longer under the pointer. */
|
|
84
|
+
export type KeyDetail = typeof SceneSchema.KeyDetail.Type;
|
|
85
|
+
|
|
86
|
+
export type ViewConfig<Message> = {
|
|
87
|
+
/** The scene to show. Derive it from the Model; unchanged scenes cost nothing. */
|
|
88
|
+
readonly scene: SceneDescription;
|
|
89
|
+
/** The Mount from `makeSceneMount`, created once at the entry point. */
|
|
90
|
+
readonly mount: SceneMount;
|
|
91
|
+
/** Maps the mount outcome (started or failed) into the app's Messages. */
|
|
92
|
+
readonly onLifecycle: (message: SceneLifecycle) => Message;
|
|
93
|
+
/** Pressed and released on the same mesh with the same button. */
|
|
94
|
+
readonly onClick?: (detail: ButtonDetail) => Message;
|
|
95
|
+
/** A double-click on the mesh that was last pressed. */
|
|
96
|
+
readonly onDoubleClick?: (detail: ButtonDetail) => Message;
|
|
97
|
+
/** A context menu on the mesh that was last pressed. The browser's menu stays shut. */
|
|
98
|
+
readonly onContextMenu?: (detail: HitDetail) => Message;
|
|
99
|
+
readonly onPointerDown?: (detail: ButtonDetail) => Message;
|
|
100
|
+
readonly onPointerUp?: (detail: ButtonDetail) => Message;
|
|
101
|
+
readonly onPointerEnter?: (detail: HitDetail) => Message;
|
|
102
|
+
/** Every pointer movement over an interactive mesh. Can fire at the pointer's rate. */
|
|
103
|
+
readonly onPointerMove?: (detail: HitDetail) => Message;
|
|
104
|
+
readonly onPointerLeave?: (detail: KeyDetail) => Message;
|
|
105
|
+
/** A click that hit no interactive mesh and moved at most two pixels: clear a selection. */
|
|
106
|
+
readonly onPointerMissed?: (detail: MissDetail) => Message;
|
|
107
|
+
/** The wheel over an interactive mesh. Page scrolling is not blocked. */
|
|
108
|
+
readonly onWheel?: (detail: WheelDetail) => Message;
|
|
109
|
+
/**
|
|
110
|
+
* The pointer moved while pressed on a mesh, anywhere (the press captures it). Turn the
|
|
111
|
+
* ray into a point with `pointOnPlane` to drag along a plane.
|
|
112
|
+
*/
|
|
113
|
+
readonly onDrag?: (detail: DragDetail) => Message;
|
|
114
|
+
/** A drag ended: released anywhere, or the pointer was cancelled. */
|
|
115
|
+
readonly onDragEnd?: (detail: DragDetail) => Message;
|
|
116
|
+
/**
|
|
117
|
+
* A drag, wheel or pinch anywhere on the canvas, reported while the scene claims
|
|
118
|
+
* gestures (`scene({ gestures: 'scene' })`). Apply it with `orbit` in `update`:
|
|
119
|
+
* the camera stays in the Model.
|
|
120
|
+
*/
|
|
121
|
+
readonly onGesture?: (gesture: Gesture) => Message;
|
|
122
|
+
/**
|
|
123
|
+
* A gesture is about to begin on a camera turning by itself (`autoRotate`): keep where
|
|
124
|
+
* it got to with `autoRotated(model.camera, detail)` (a view's camera when `view` names
|
|
125
|
+
* one), so the gesture goes on from where the camera was shown.
|
|
126
|
+
*/
|
|
127
|
+
readonly onAutoRotated?: (detail: AutoRotatedDetail) => Message;
|
|
128
|
+
/**
|
|
129
|
+
* Two bodies began or stopped touching (react-three-rapier's `onCollisionEnter` and
|
|
130
|
+
* `onCollisionExit`, for the whole scene). Needs `physics` and an engine.
|
|
131
|
+
*/
|
|
132
|
+
readonly onCollision?: (detail: CollisionDetail) => Message;
|
|
133
|
+
/**
|
|
134
|
+
* Two bodies pushed on each other harder than a body's `contactForces` threshold during
|
|
135
|
+
* a step (react-three-rapier's `onContactForce`). Rapier reports every such step, so
|
|
136
|
+
* set the threshold high.
|
|
137
|
+
*/
|
|
138
|
+
readonly onContactForce?: (detail: ContactForceDetail) => Message;
|
|
139
|
+
/**
|
|
140
|
+
* A node listed in the scene's `measure` has a new world bounding box (Drei's `Bounds`
|
|
141
|
+
* measuring, as a Message): fit the camera to it with `fitCamera`.
|
|
142
|
+
*/
|
|
143
|
+
readonly onMeasured?: (detail: MeasuredDetail) => Message;
|
|
144
|
+
/**
|
|
145
|
+
* A measured label (`scene({ measure })`) was laid out anew: its block and tight glyph
|
|
146
|
+
* bounds in its own units, before its scale, to size what goes behind it.
|
|
147
|
+
*/
|
|
148
|
+
readonly onLabelMeasured?: (detail: LabelMeasuredDetail) => Message;
|
|
149
|
+
/**
|
|
150
|
+
* An immersive session began presenting this scene (`mode`), or ended (`mode: null`),
|
|
151
|
+
* whether the app or the user (through the headset) ended it.
|
|
152
|
+
*/
|
|
153
|
+
readonly onXRSession?: (detail: XrSessionDetail) => Message;
|
|
154
|
+
/**
|
|
155
|
+
* In a session, a thumbstick pushed past halfway in a direction, or let back to the
|
|
156
|
+
* centre: snap turn and teleport by changing the scene's `xr({ heading, origin })`, as
|
|
157
|
+
* pmndrs/xr's controller locomotion does.
|
|
158
|
+
*/
|
|
159
|
+
readonly onXRStick?: (detail: XrStickDetail) => Message;
|
|
160
|
+
/** In a session, a controller button pressed or released (`xr-standard` mapping). */
|
|
161
|
+
readonly onXRButton?: (detail: XrButtonDetail) => Message;
|
|
162
|
+
/**
|
|
163
|
+
* In AR with `xr({ hitTest: true })`, a select whose ray met a real surface: place
|
|
164
|
+
* things there.
|
|
165
|
+
*/
|
|
166
|
+
readonly onXRSurface?: (detail: XrSurfaceDetail) => Message;
|
|
167
|
+
/**
|
|
168
|
+
* Gliding with the sticks (`xr({ glide })`) stopped: keep where the player now stands
|
|
169
|
+
* with `glided(model.xr, detail)`.
|
|
170
|
+
*/
|
|
171
|
+
readonly onXRMoved?: (detail: XrMovedDetail) => Message;
|
|
172
|
+
/**
|
|
173
|
+
* With `xr({ planes: true })`, the detected planes changed (found, reshaped, lost, or
|
|
174
|
+
* seen from where the player moved to): every plane, as it is now. Follow one with
|
|
175
|
+
* `group({ follow: planeSpace(id) })`.
|
|
176
|
+
*/
|
|
177
|
+
readonly onXRPlanes?: (detail: XrPlanesDetail) => Message;
|
|
178
|
+
/**
|
|
179
|
+
* With `xr({ meshes: true })`, the detected meshes changed: every mesh, as it is now.
|
|
180
|
+
* Draw one as `mesh({ geometry: xrMesh(id), material })` inside a group that follows
|
|
181
|
+
* `meshSpace(id)`: its geometry is lent to the scene, not sent.
|
|
182
|
+
*/
|
|
183
|
+
readonly onXRMeshes?: (detail: XrMeshesDetail) => Message;
|
|
184
|
+
/**
|
|
185
|
+
* Frames ran persistently slow or fast, and the performance factor moved (0 to 1).
|
|
186
|
+
* Store it in the Model and lower `render.resolution` with it, as Drei's
|
|
187
|
+
* `PerformanceMonitor` and `AdaptiveDpr` do together.
|
|
188
|
+
*/
|
|
189
|
+
readonly onPerformanceChange?: (detail: PerformanceDetail) => Message;
|
|
190
|
+
/** A scene update was rejected; the scene keeps its last good state. */
|
|
191
|
+
readonly onError?: (detail: { readonly error: SceneError }) => Message;
|
|
192
|
+
/**
|
|
193
|
+
* What each scene update did (operations by kind) and the GPU resources held afterwards.
|
|
194
|
+
* Fires at startup and after every update that changed something, never for no-ops.
|
|
195
|
+
*/
|
|
196
|
+
readonly onReconciled?: (detail: ReconciledDetail) => Message;
|
|
197
|
+
/**
|
|
198
|
+
* HTML at points in the scene, as Drei's `Html`: each `overlay` says where (a point,
|
|
199
|
+
* perhaps on a node), `content` what. The scene moves the HTML there every frame it
|
|
200
|
+
* draws; the content is ordinary view HTML, rendered from the Model.
|
|
201
|
+
*/
|
|
202
|
+
readonly overlays?: ReadonlyArray<OverlayView>;
|
|
203
|
+
readonly className?: string;
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
const given = Option.fromNullishOr;
|
|
207
|
+
|
|
208
|
+
/** Renders a scene element. Only the event handlers you pass are bound. */
|
|
209
|
+
export const view = <Message>(
|
|
210
|
+
config: ViewConfig<NoInfer<Message>>,
|
|
211
|
+
h: HtmlBuilder<Message>,
|
|
212
|
+
): Html => {
|
|
213
|
+
const element = sceneElement.withMessage(h);
|
|
214
|
+
const optional = Arr.getSomes([
|
|
215
|
+
Option.map(given(config.onClick), element.OnSceneClick),
|
|
216
|
+
Option.map(given(config.onDoubleClick), element.OnSceneDoubleClick),
|
|
217
|
+
Option.map(given(config.onContextMenu), element.OnSceneContextMenu),
|
|
218
|
+
Option.map(given(config.onPointerDown), element.OnScenePointerDown),
|
|
219
|
+
Option.map(given(config.onPointerUp), element.OnScenePointerUp),
|
|
220
|
+
Option.map(given(config.onPointerEnter), element.OnScenePointerEnter),
|
|
221
|
+
Option.map(given(config.onPointerMove), element.OnScenePointerMove),
|
|
222
|
+
Option.map(given(config.onPointerLeave), element.OnScenePointerLeave),
|
|
223
|
+
Option.map(given(config.onPointerMissed), element.OnScenePointerMissed),
|
|
224
|
+
Option.map(given(config.onWheel), element.OnSceneWheel),
|
|
225
|
+
Option.map(given(config.onDrag), element.OnSceneDrag),
|
|
226
|
+
Option.map(given(config.onDragEnd), element.OnSceneDragEnd),
|
|
227
|
+
Option.map(given(config.onCollision), element.OnSceneCollision),
|
|
228
|
+
Option.map(given(config.onContactForce), element.OnSceneContactForce),
|
|
229
|
+
Option.map(given(config.onMeasured), element.OnSceneMeasured),
|
|
230
|
+
Option.map(given(config.onLabelMeasured), element.OnSceneLabelMeasured),
|
|
231
|
+
Option.map(given(config.onXRSession), element.OnSceneXr),
|
|
232
|
+
Option.map(given(config.onXRStick), element.OnSceneXrStick),
|
|
233
|
+
Option.map(given(config.onXRButton), element.OnSceneXrButton),
|
|
234
|
+
Option.map(given(config.onXRSurface), element.OnSceneXrSurface),
|
|
235
|
+
Option.map(given(config.onXRMoved), element.OnSceneXrMoved),
|
|
236
|
+
Option.map(given(config.onXRPlanes), element.OnSceneXrPlanes),
|
|
237
|
+
Option.map(given(config.onXRMeshes), element.OnSceneXrMeshes),
|
|
238
|
+
Option.map(given(config.onAutoRotated), element.OnSceneAutoRotated),
|
|
239
|
+
Option.map(given(config.onGesture), (toMessage) =>
|
|
240
|
+
element.OnSceneGesture((detail) => toMessage(detail.gesture)),
|
|
241
|
+
),
|
|
242
|
+
Option.map(given(config.onPerformanceChange), element.OnScenePerformance),
|
|
243
|
+
Option.map(given(config.onError), element.OnSceneError),
|
|
244
|
+
Option.map(given(config.onReconciled), element.OnSceneReconciled),
|
|
245
|
+
Option.map(given(config.className), h.Class),
|
|
246
|
+
]);
|
|
247
|
+
const lifecycle = Mount.mapMessage(config.mount(), config.onLifecycle);
|
|
248
|
+
const overlays = config.overlays ?? [];
|
|
249
|
+
const scene = markTyped(withOverlays(config.scene, overlays));
|
|
250
|
+
return element(
|
|
251
|
+
[element.Scene(scene), h.OnMount(lifecycle), ...optional],
|
|
252
|
+
overlayLayer(h, overlays),
|
|
253
|
+
);
|
|
254
|
+
};
|
package/src/xr.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Immersive sessions as Effects an application runs from Foldkit Commands: whether a mode
|
|
3
|
+
* is supported, entering it from a user gesture, and leaving it. The scene presented is
|
|
4
|
+
* the mounted one whose description has `xr`; while presenting, the session drives its
|
|
5
|
+
* frames and the view's `onXRSession` reports the session starting and ending.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import { Data, Effect } from 'effect';
|
|
10
|
+
|
|
11
|
+
import type { XrHub, XrMode } from './dom/xrHub.ts';
|
|
12
|
+
|
|
13
|
+
/** A session could not start: no WebXR, no scene describing `xr`, or the request refused. */
|
|
14
|
+
export class XrUnavailable extends Data.TaggedError('XrUnavailable')<{
|
|
15
|
+
readonly mode: XrMode;
|
|
16
|
+
readonly cause: unknown;
|
|
17
|
+
}> {}
|
|
18
|
+
|
|
19
|
+
/** The immersive session controls for an environment's hub. */
|
|
20
|
+
export const makeXr = (hub: XrHub) => ({
|
|
21
|
+
/** Whether the browser can present `mode` (false without WebXR). */
|
|
22
|
+
supported: (mode: XrMode) => Effect.promise(() => hub.supported(mode)),
|
|
23
|
+
/**
|
|
24
|
+
* Enters `mode`, presenting the mounted scene that describes `xr`. Run it from the
|
|
25
|
+
* Command a click (or other user gesture) produces: browsers require one.
|
|
26
|
+
*/
|
|
27
|
+
enter: (mode: XrMode) =>
|
|
28
|
+
Effect.tryPromise({
|
|
29
|
+
try: () => hub.enter(mode),
|
|
30
|
+
catch: (cause) => new XrUnavailable({ mode, cause }),
|
|
31
|
+
}),
|
|
32
|
+
/** Ends the session, if there is one. */
|
|
33
|
+
exit: Effect.promise(() => hub.exit()),
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
export type XrControls = ReturnType<typeof makeXr>;
|