@reactvision/react-viro 2.55.0 → 2.57.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.
Files changed (94) hide show
  1. package/README.md +27 -0
  2. package/android/react_viro/react_viro-release.aar +0 -0
  3. package/android/viro_renderer/viro_renderer-release.aar +0 -0
  4. package/components/AR/ViroARScene.tsx +17 -0
  5. package/components/AR/ViroARSceneNavigator.tsx +33 -0
  6. package/components/Utilities/StreamingAudioManager.ts +73 -0
  7. package/components/Utilities/ViroVersion.ts +1 -1
  8. package/components/ViroCameraTexture.tsx +230 -0
  9. package/components/ViroGameLoop.tsx +74 -0
  10. package/components/ViroGameLoopUtils.ts +52 -0
  11. package/components/ViroObjectDetector.tsx +222 -0
  12. package/components/ViroVirtualButton.tsx +101 -0
  13. package/components/ViroVirtualJoystick.tsx +126 -0
  14. package/components/hooks/useGameLoop.ts +78 -0
  15. package/dist/components/AR/ViroARScene.d.ts +12 -0
  16. package/dist/components/AR/ViroARScene.js +8 -1
  17. package/dist/components/AR/ViroARSceneNavigator.d.ts +30 -0
  18. package/dist/components/Utilities/StreamingAudioManager.d.ts +22 -0
  19. package/dist/components/Utilities/StreamingAudioManager.js +45 -0
  20. package/dist/components/Utilities/ViroVersion.d.ts +1 -1
  21. package/dist/components/Utilities/ViroVersion.js +1 -1
  22. package/dist/components/ViroCameraTexture.d.ts +137 -0
  23. package/dist/components/ViroCameraTexture.js +170 -0
  24. package/dist/components/ViroGameLoop.d.ts +31 -0
  25. package/dist/components/ViroGameLoop.js +31 -0
  26. package/dist/components/ViroGameLoopUtils.d.ts +26 -0
  27. package/dist/components/ViroGameLoopUtils.js +44 -0
  28. package/dist/components/ViroObjectDetector.d.ts +138 -0
  29. package/dist/components/ViroObjectDetector.js +85 -0
  30. package/dist/components/ViroVirtualButton.d.ts +76 -0
  31. package/dist/components/ViroVirtualButton.js +62 -0
  32. package/dist/components/ViroVirtualJoystick.d.ts +85 -0
  33. package/dist/components/ViroVirtualJoystick.js +72 -0
  34. package/dist/components/hooks/useGameLoop.d.ts +34 -0
  35. package/dist/components/hooks/useGameLoop.js +63 -0
  36. package/dist/index.d.ts +11 -1
  37. package/dist/index.js +20 -2
  38. package/dist/plugins/withViro.js +2 -15
  39. package/dist/plugins/withViroAndroid.js +52 -15
  40. package/dist/plugins/withViroIos.js +0 -6
  41. package/index.ts +21 -0
  42. package/ios/ViroReact.podspec +25 -6
  43. package/ios/dist/ViroRenderer/ViroKit.framework/ARCoreCoreMLSemanticsResources.bundle/Info.plist +0 -0
  44. package/ios/dist/ViroRenderer/ViroKit.framework/ARCoreResources.bundle/Info.plist +0 -0
  45. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROARDepthMesh.h +9 -1
  46. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROARFrame.h +33 -0
  47. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROARFrameiOS.h +26 -0
  48. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROARScene.h +15 -1
  49. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROARSceneDelegateiOS.h +8 -0
  50. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROARSession.h +7 -0
  51. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROARSessioniOS.h +18 -0
  52. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROARWorldMesh.h +111 -8
  53. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROAudioPlayer.h +19 -0
  54. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROAudioPlayerStreamiOS.h +95 -0
  55. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROCameraTextureiOS.h +17 -1
  56. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VRODriver.h +20 -0
  57. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VRODynamicGeometry.h +170 -0
  58. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VRODynamicMeshNode.h +87 -0
  59. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROGLTFLoader.h +5 -1
  60. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROGameLoopListener.h +125 -0
  61. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROInputState.h +133 -0
  62. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROMonocularDepthEstimator.h +73 -0
  63. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROObjectDetector.h +134 -0
  64. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROPCMRingBuffer.h +143 -0
  65. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROPencil.h +9 -0
  66. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VRORenderContext.h +4 -0
  67. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VRORenderer.h +1 -0
  68. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROVertexBuffer.h +102 -0
  69. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROViewAR.h +9 -0
  70. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROVirtualControllerRegistry.h +106 -0
  71. package/ios/dist/ViroRenderer/ViroKit.framework/Info.plist +0 -0
  72. package/ios/dist/ViroRenderer/ViroKit.framework/Shaders.dat +1 -1
  73. package/ios/dist/ViroRenderer/ViroKit.framework/ViroKit +0 -0
  74. package/ios/dist/ViroRenderer/ViroKit.framework/constant_fsh.glsl +1 -0
  75. package/ios/dist/ViroRenderer/ViroKit.framework/constant_fsh_adreno.glsl +1 -0
  76. package/ios/dist/ViroRenderer/ViroKit.framework/standard_fsh.glsl +1 -0
  77. package/ios/dist/ViroRenderer/ViroKit.framework/standard_vsh.glsl +4 -0
  78. package/ios/dist/ViroRenderer/ViroKit.framework/standard_vsh_adreno.glsl +4 -0
  79. package/ios/dist/ViroRendererVisionOS/ViroKit.podspec +27 -0
  80. package/ios/dist/include/VRTARScene.h +1 -0
  81. package/ios/dist/include/VRTARSceneNavigator.h +3 -0
  82. package/ios/dist/include/VRTCameraTexture.h +62 -0
  83. package/ios/dist/include/VRTCameraTextureManager.h +30 -0
  84. package/ios/dist/include/VRTCameraTextureModule.h +38 -0
  85. package/ios/dist/include/VRTGameLoopView.h +19 -0
  86. package/ios/dist/include/VRTGameLoopViewManager.h +8 -0
  87. package/ios/dist/include/VRTManagedAnimation.h +67 -0
  88. package/ios/dist/include/VRTStreamingAudioModule.h +21 -0
  89. package/ios/dist/include/VRTVirtualButtonView.h +47 -0
  90. package/ios/dist/include/VRTVirtualButtonViewManager.h +21 -0
  91. package/ios/dist/include/VRTVirtualJoystickView.h +59 -0
  92. package/ios/dist/include/VRTVirtualJoystickViewManager.h +26 -0
  93. package/ios/dist/lib/libViroReact.a +0 -0
  94. package/package.json +17 -20
@@ -0,0 +1,222 @@
1
+ /**
2
+ * Copyright (c) 2026-present, ReactVision, Inc.
3
+ * All rights reserved.
4
+ *
5
+ * This source code is licensed under the BSD-style license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+
9
+ import * as React from "react";
10
+ import {
11
+ NativeSyntheticEvent,
12
+ requireNativeComponent,
13
+ ViewProps,
14
+ } from "react-native";
15
+
16
+ // ---------------------------------------------------------------------------
17
+ // Types
18
+ // ---------------------------------------------------------------------------
19
+
20
+ export type ViroDetectorMode = "prompt-free" | "text" | "visual";
21
+
22
+ export type ViroDetectionBoundingBox = {
23
+ /** Left edge, normalized [0, 1] */
24
+ x: number;
25
+ /** Top edge, normalized [0, 1] */
26
+ y: number;
27
+ width: number;
28
+ height: number;
29
+ };
30
+
31
+ export type ViroDetectedObject = {
32
+ label: string;
33
+ confidence: number;
34
+ boundingBox: ViroDetectionBoundingBox;
35
+ /** 3D world position (metres), from raycasting the bbox centre. Present when `projectToWorld` is true (iOS only). */
36
+ worldPosition?: { x: number; y: number; z: number };
37
+ /**
38
+ * Bounding box in density-independent points (dp), aligned to the on-screen AR
39
+ * camera preview. Use directly as { left, top, width, height } in an
40
+ * absolute-positioned View. Present on iOS and Android.
41
+ */
42
+ screenBoundingBox?: { x: number; y: number; width: number; height: number };
43
+ };
44
+
45
+ export type ViroDetectionEvent = {
46
+ detections: ViroDetectedObject[];
47
+ };
48
+
49
+ export type ViroDetectorReadyEvent = Record<string, never>;
50
+
51
+ export type ViroDetectorErrorEvent = {
52
+ error: string;
53
+ };
54
+
55
+ type Props = ViewProps & {
56
+ /**
57
+ * The YOLOE model to run. Either a bundled model **name** (resolved natively as
58
+ * `<name>.onnx` in the app bundle / Android assets), or an absolute `/`-path or
59
+ * `file://` URL to an `.onnx` file. Defaults to "yoloe-26s".
60
+ */
61
+ model?: string;
62
+
63
+ /**
64
+ * Inference mode:
65
+ * - "prompt-free": YOLOE LRPC — detects 4,585 categories with no extra prompt.
66
+ * - "text": YOLOE RepRTA — detect only the classes listed in `categories`.
67
+ * - "visual": YOLOE SAVPE — detect objects similar to a reference crop
68
+ * (reference image API to be added in a future release).
69
+ * Defaults to "prompt-free".
70
+ */
71
+ mode?: ViroDetectorMode;
72
+
73
+ /**
74
+ * Text categories for "text" mode, e.g. ["chair", "person", "laptop"].
75
+ * Ignored in other modes.
76
+ */
77
+ categories?: string[];
78
+
79
+ /**
80
+ * Minimum confidence score [0, 1] for a detection to be reported.
81
+ * Defaults to 0.4.
82
+ */
83
+ confidenceThreshold?: number;
84
+
85
+ /**
86
+ * IoU threshold used by NMS post-processing.
87
+ * Defaults to 0.45.
88
+ */
89
+ iouThreshold?: number;
90
+
91
+ /**
92
+ * Maximum number of inference calls per second. The camera runs at the
93
+ * device's native frame rate; this throttle prevents the inference thread
94
+ * from saturating the CPU/NPU while the AR renderer runs in parallel.
95
+ * Defaults to 15.
96
+ */
97
+ maxFPS?: number;
98
+
99
+ /**
100
+ * Maximum number of detections reported per frame, kept as the top-N by
101
+ * confidence (after NMS). Lower this to reduce clutter, raise it to surface
102
+ * more objects.
103
+ * Defaults to 20.
104
+ */
105
+ maxDetections?: number;
106
+
107
+ /**
108
+ * When true, each detection includes a `worldPosition` {x, y, z} obtained by
109
+ * raycasting the bbox centre against the AR scene. iOS only (Android emits
110
+ * `screenBoundingBox` but not yet `worldPosition`).
111
+ * Defaults to true.
112
+ */
113
+ projectToWorld?: boolean;
114
+
115
+ /**
116
+ * Called every time the detector produces a new set of detections.
117
+ * May be called with an empty array if nothing is detected in a frame.
118
+ */
119
+ onDetection?: (event: ViroDetectionEvent) => void;
120
+
121
+ /**
122
+ * Called once the model has been loaded and the camera pipeline is running.
123
+ */
124
+ onReady?: (event: ViroDetectorReadyEvent) => void;
125
+
126
+ /**
127
+ * Called if the model fails to load or the camera cannot be opened.
128
+ */
129
+ onError?: (event: ViroDetectorErrorEvent) => void;
130
+ };
131
+
132
+ // ---------------------------------------------------------------------------
133
+ // Internal native event types
134
+ // ---------------------------------------------------------------------------
135
+
136
+ type NativeDetectionEvent = NativeSyntheticEvent<ViroDetectionEvent>;
137
+ type NativeReadyEvent = NativeSyntheticEvent<ViroDetectorReadyEvent>;
138
+ type NativeErrorEvent = NativeSyntheticEvent<ViroDetectorErrorEvent>;
139
+
140
+ // ---------------------------------------------------------------------------
141
+ // Component
142
+ // ---------------------------------------------------------------------------
143
+
144
+ const VRTObjectDetectorView = requireNativeComponent<any>("VRTObjectDetectorView");
145
+
146
+ /**
147
+ * ViroObjectDetector — on-device open-vocabulary object detection powered by YOLOE.
148
+ *
149
+ * Runs **only in AR**: it shares the camera feed of the enclosing
150
+ * `ViroARSceneNavigator` (no separate camera session, no preview of its own) and
151
+ * fires `onDetection` with labels, normalized bounding boxes, and an on-screen
152
+ * `screenBoundingBox` (dp) at up to `maxFPS`. Mount it as a child or sibling of a
153
+ * `ViroARSceneNavigator`; it renders nothing itself, so give it `width: 0, height: 0`.
154
+ *
155
+ * @example
156
+ * ```tsx
157
+ * <ViroARSceneNavigator initialScene={{ scene: MyScene }} />
158
+ * <ViroObjectDetector
159
+ * style={{ position: "absolute", width: 0, height: 0 }}
160
+ * mode="prompt-free"
161
+ * confidenceThreshold={0.4}
162
+ * maxFPS={15}
163
+ * onDetection={({ detections }) => {
164
+ * detections.forEach(d => console.log(d.label, d.confidence, d.screenBoundingBox));
165
+ * }}
166
+ * />
167
+ * ```
168
+ */
169
+ export const ViroObjectDetector: React.FC<Props> = ({
170
+ model = "yoloe-26s",
171
+ mode = "prompt-free",
172
+ categories = [],
173
+ confidenceThreshold = 0.4,
174
+ iouThreshold = 0.45,
175
+ maxFPS = 15,
176
+ maxDetections = 20,
177
+ projectToWorld = true,
178
+ onDetection,
179
+ onReady,
180
+ onError,
181
+ style,
182
+ ...rest
183
+ }) => {
184
+ const handleDetection = React.useCallback(
185
+ (event: NativeDetectionEvent) => {
186
+ onDetection?.(event.nativeEvent);
187
+ },
188
+ [onDetection]
189
+ );
190
+
191
+ const handleReady = React.useCallback(
192
+ (event: NativeReadyEvent) => {
193
+ onReady?.(event.nativeEvent);
194
+ },
195
+ [onReady]
196
+ );
197
+
198
+ const handleError = React.useCallback(
199
+ (event: NativeErrorEvent) => {
200
+ onError?.(event.nativeEvent);
201
+ },
202
+ [onError]
203
+ );
204
+
205
+ return (
206
+ <VRTObjectDetectorView
207
+ {...rest}
208
+ style={style}
209
+ model={model}
210
+ mode={mode}
211
+ categories={categories}
212
+ confidenceThreshold={confidenceThreshold}
213
+ iouThreshold={iouThreshold}
214
+ maxFPS={maxFPS}
215
+ maxDetections={maxDetections}
216
+ projectToWorld={projectToWorld}
217
+ onDetectionViro={onDetection ? handleDetection : undefined}
218
+ onReadyViro={onReady ? handleReady : undefined}
219
+ onErrorViro={onError ? handleError : undefined}
220
+ />
221
+ );
222
+ };
@@ -0,0 +1,101 @@
1
+ /**
2
+ * ViroVirtualButton.tsx
3
+ *
4
+ * Copyright © 2026 ReactVision. All rights reserved.
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining
7
+ * a copy of this software and associated documentation files (the
8
+ * "Software"), to deal in the Software without restriction, including
9
+ * without limitation the rights to use, copy, modify, merge, publish,
10
+ * distribute, sublicense, and/or sell copies of the Software, and to
11
+ * permit persons to whom the Software is furnished to do so, subject to
12
+ * the following conditions:
13
+ *
14
+ * The above copyright notice and this permission notice shall be included
15
+ * in all copies or substantial portions of the Software.
16
+ */
17
+
18
+ import * as React from "react";
19
+ import {
20
+ processColor,
21
+ requireNativeComponent,
22
+ StyleProp,
23
+ ViewStyle,
24
+ } from "react-native";
25
+
26
+ /**
27
+ * Native virtual-button component. Renders an on-screen circular button labelled
28
+ * with the button name (e.g. "A", "B"). On press it writes setButton(idx, true)
29
+ * to a VROInputState in VROVirtualControllerRegistry; on release it writes false.
30
+ *
31
+ * Like ViroVirtualJoystick, touch handling is entirely native — no JS bridge
32
+ * round-trip — so press/release latency stays in the single-digit-milliseconds range.
33
+ *
34
+ * Example:
35
+ *
36
+ * <ViroVirtualButton
37
+ * controllerId="p1"
38
+ * button="A"
39
+ * style={{ position: "absolute", bottom: 60, right: 60, width: 60, height: 60 }}
40
+ * size={44}
41
+ * tintColor="rgba(255,255,255,0.6)"
42
+ * />
43
+ */
44
+
45
+ export type ViroButtonName =
46
+ | "A" | "B" | "X" | "Y" | "Z"
47
+ | "L1" | "R1" | "L2" | "R2"
48
+ | "Start" | "Select";
49
+
50
+ export interface ViroVirtualButtonProps {
51
+ /**
52
+ * Identifier of the controller this button writes to. Must match the
53
+ * `controllerId` of any joystick or other adapter sharing the same state.
54
+ */
55
+ controllerId: string;
56
+
57
+ /**
58
+ * Which button this view maps to. Determines the index written to
59
+ * VROInputState::setButton and the label drawn on the circle.
60
+ */
61
+ button: ViroButtonName;
62
+
63
+ /**
64
+ * Diameter of the button circle in points. Defaults to 44.
65
+ */
66
+ size?: number;
67
+
68
+ /**
69
+ * Fill colour of the circle. Any value accepted by React Native's color prop.
70
+ * Defaults to `rgba(255, 255, 255, 0.6)`.
71
+ */
72
+ tintColor?: number | string;
73
+
74
+ /** Fired when the button is pressed down (same tick as C++ VROInputState write). */
75
+ onPressIn?: (event: { nativeEvent: { button: string } }) => void;
76
+ /** Fired when the button is released (same tick as C++ VROInputState write). */
77
+ onPressOut?: (event: { nativeEvent: { button: string } }) => void;
78
+
79
+ /**
80
+ * Positioning / sizing.
81
+ */
82
+ style?: StyleProp<ViewStyle>;
83
+ }
84
+
85
+ const NativeVirtualButton =
86
+ requireNativeComponent<any>("VRTVirtualButtonView");
87
+
88
+ export const ViroVirtualButton: React.FC<ViroVirtualButtonProps> = (props) => {
89
+ const { tintColor, ...rest } = props;
90
+ const processedTint =
91
+ tintColor != null ? processColor(tintColor) : undefined;
92
+
93
+ return (
94
+ <NativeVirtualButton
95
+ {...rest}
96
+ tintColor={processedTint}
97
+ />
98
+ );
99
+ };
100
+
101
+ export default ViroVirtualButton;
@@ -0,0 +1,126 @@
1
+ /**
2
+ * ViroVirtualJoystick.tsx
3
+ *
4
+ * Copyright © 2026 ReactVision. All rights reserved.
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining
7
+ * a copy of this software and associated documentation files (the
8
+ * "Software"), to deal in the Software without restriction, including
9
+ * without limitation the rights to use, copy, modify, merge, publish,
10
+ * distribute, sublicense, and/or sell copies of the Software, and to
11
+ * permit persons to whom the Software is furnished to do so, subject to
12
+ * the following conditions:
13
+ *
14
+ * The above copyright notice and this permission notice shall be included
15
+ * in all copies or substantial portions of the Software.
16
+ */
17
+
18
+ import * as React from "react";
19
+ import {
20
+ Platform,
21
+ processColor,
22
+ requireNativeComponent,
23
+ StyleProp,
24
+ ViewStyle,
25
+ } from "react-native";
26
+
27
+ /**
28
+ * Native virtual-joystick component. Renders an on-screen analog stick whose
29
+ * deflection is written to a process-wide controller state identified by
30
+ * `controllerId`. The touch handling and state update happen entirely on the
31
+ * native side — events do not cross the JS bridge — so input latency stays in
32
+ * the single-digit-milliseconds range even under heavy JS load.
33
+ *
34
+ * Consumers read the resulting controller state from native code (via a
35
+ * VROFrameListener that calls VROVirtualControllerRegistry::instance().peek(id))
36
+ * or from JS via the forthcoming useVirtualController hook.
37
+ *
38
+ * Multiple input sources (this joystick, a future MFi-gamepad adapter, a tilt
39
+ * adapter) can target the same `controllerId`; the controller state aggregates
40
+ * whichever sources last wrote to each field.
41
+ *
42
+ * Example:
43
+ *
44
+ * <ViroVirtualJoystick
45
+ * controllerId="p1"
46
+ * stickSide="left"
47
+ * style={{ position: "absolute", bottom: 40, left: 40, width: 140, height: 140 }}
48
+ * radius={60}
49
+ * tintColor="rgba(255,255,255,0.6)"
50
+ * />
51
+ */
52
+
53
+ export type ViroStickSide = "left" | "right";
54
+
55
+ export interface ViroVirtualJoystickProps {
56
+ /**
57
+ * Identifier of the controller this joystick writes to. Matches the id used
58
+ * by readers (e.g. native simulation adapters) and by other input adapters
59
+ * targeting the same controller.
60
+ */
61
+ controllerId: string;
62
+
63
+ /**
64
+ * Which stick on the controller this joystick drives. Defaults to `"left"`.
65
+ */
66
+ stickSide?: ViroStickSide;
67
+
68
+ /**
69
+ * Outer ring radius in points (the knob is clamped to this distance from the
70
+ * centre). The view's frame should be at least `radius * 2 + knobMargin` on
71
+ * each axis for the stick to render fully inside its bounds. Defaults to 60.
72
+ */
73
+ radius?: number;
74
+
75
+ /**
76
+ * Tint colour applied to both the outer ring stroke and the knob fill. Any
77
+ * value accepted by React Native's color prop. Defaults to
78
+ * `rgba(255, 255, 255, 0.6)`.
79
+ */
80
+ tintColor?: number | string;
81
+
82
+ /**
83
+ * Fired on every stick movement with { x, y } normalised to [-1, 1].
84
+ * Called from native synchronously after VROInputState is updated,
85
+ * so you can drive JS visuals from the same user gesture.
86
+ */
87
+ onStickChange?: (event: { nativeEvent: { x: number; y: number } }) => void;
88
+
89
+ /**
90
+ * Positioning / sizing. Same prop as any RN view.
91
+ */
92
+ style?: StyleProp<ViewStyle>;
93
+ }
94
+
95
+ const NativeVirtualJoystick =
96
+ requireNativeComponent<any>("VRTVirtualJoystickView");
97
+
98
+ export const ViroVirtualJoystick: React.FC<ViroVirtualJoystickProps> = (
99
+ props,
100
+ ) => {
101
+ const { tintColor, onStickChange, ...rest } = props;
102
+ const processedTint =
103
+ tintColor != null ? processColor(tintColor) : undefined;
104
+
105
+ // Native sends x/y as strings to avoid Fabric conversions.h type-check spam.
106
+ const handleStickChange = onStickChange
107
+ ? (e: any) => onStickChange({
108
+ nativeEvent: { x: parseFloat(e.nativeEvent.x), y: parseFloat(e.nativeEvent.y) }
109
+ })
110
+ : undefined;
111
+
112
+ return (
113
+ <NativeVirtualJoystick
114
+ {...rest}
115
+ tintColor={processedTint}
116
+ onStickChange={handleStickChange}
117
+ />
118
+ );
119
+ };
120
+
121
+ if (Platform.OS === "web") {
122
+ // No-op on web; the joystick is a native-only view today.
123
+ // Tracked as a follow-up: HTML/Canvas equivalent for the wasm path.
124
+ }
125
+
126
+ export default ViroVirtualJoystick;
@@ -0,0 +1,78 @@
1
+ /**
2
+ * useGameLoop — convenience hook that wraps <ViroGameLoop />.
3
+ *
4
+ * NOTE: This hook does NOT imperatively mount a component. It is designed to
5
+ * be used alongside a <ViroGameLoop> component in the scene. The component
6
+ * fires the events; the hook subscribes to them via a shared ref pattern.
7
+ *
8
+ * For the most common case — one game loop per scene — use <ViroGameLoop>
9
+ * directly with onUpdate/onFixedUpdate props. The hook is provided as
10
+ * syntactic sugar for cases where the callback needs to live deep in the tree.
11
+ *
12
+ * Copyright © 2026 ReactVision. All rights reserved.
13
+ */
14
+
15
+ import { useEffect, useRef } from "react";
16
+ import type {
17
+ ViroGameLoopUpdateEvent,
18
+ ViroGameLoopFixedEvent,
19
+ } from "../ViroGameLoop";
20
+
21
+ export type UseGameLoopOptions = {
22
+ /** Fixed simulation frequency in Hz. Requires a ViroGameLoop with matching fixedHz. */
23
+ fixedHz?: number;
24
+ };
25
+
26
+ /**
27
+ * Subscribe to the variable-step update from the nearest <ViroGameLoop>.
28
+ *
29
+ * The callback is always current (uses a ref internally) so it is safe to
30
+ * reference component state without adding it to a dependency array.
31
+ */
32
+ export function useGameLoop(
33
+ callback: (event: ViroGameLoopUpdateEvent) => void,
34
+ _options?: UseGameLoopOptions
35
+ ): (event: ViroGameLoopUpdateEvent) => void {
36
+ const cbRef = useRef(callback);
37
+ useEffect(() => {
38
+ cbRef.current = callback;
39
+ });
40
+ // Return a stable handler to pass to ViroGameLoop's onUpdate prop
41
+ const handlerRef = useRef((event: ViroGameLoopUpdateEvent) => {
42
+ cbRef.current(event);
43
+ });
44
+ return handlerRef.current;
45
+ }
46
+
47
+ /**
48
+ * Subscribe to the late-update (post-physics) callback.
49
+ */
50
+ export function useLateUpdate(
51
+ callback: (event: ViroGameLoopUpdateEvent) => void
52
+ ): (event: ViroGameLoopUpdateEvent) => void {
53
+ const cbRef = useRef(callback);
54
+ useEffect(() => {
55
+ cbRef.current = callback;
56
+ });
57
+ const handlerRef = useRef((event: ViroGameLoopUpdateEvent) => {
58
+ cbRef.current(event);
59
+ });
60
+ return handlerRef.current;
61
+ }
62
+
63
+ /**
64
+ * Subscribe to the fixed-step callback.
65
+ * Requires a <ViroGameLoop fixedHz={N}> in the scene.
66
+ */
67
+ export function useFixedUpdate(
68
+ callback: (event: ViroGameLoopFixedEvent) => void
69
+ ): (event: ViroGameLoopFixedEvent) => void {
70
+ const cbRef = useRef(callback);
71
+ useEffect(() => {
72
+ cbRef.current = callback;
73
+ });
74
+ const handlerRef = useRef((event: ViroGameLoopFixedEvent) => {
75
+ cbRef.current(event);
76
+ });
77
+ return handlerRef.current;
78
+ }
@@ -17,6 +17,14 @@ type Props = ViroCommonProps & {
17
17
  onTrackingUpdated?: (state: ViroTrackingState, reason: ViroTrackingReason) => void;
18
18
  onPlatformUpdate?: (platformInfoViro: ViroPlatformInfo) => void;
19
19
  onAmbientLightUpdate?: (update: ViroAmbientLightInfo) => void;
20
+ /**
21
+ * Fired once when depth data (LiDAR or monocular depth estimation) first becomes
22
+ * available for the session — i.e. hit tests can now return DepthPoints. Useful for
23
+ * hiding an "initializing depth…" state instead of acting on early, inaccurate
24
+ * feature points. On devices using monocular depth estimation, this can take a few
25
+ * seconds after the AR screen appears while the model warms up.
26
+ */
27
+ onDepthReady?: () => void;
20
28
  /**
21
29
  * Describes the acoustic properties of the room around the user
22
30
  */
@@ -47,6 +55,10 @@ export declare class ViroARScene extends ViroBase<Props> {
47
55
  * Returns object w/ "intensity" and "color" keys
48
56
  */
49
57
  _onAmbientLightUpdate: (event: NativeSyntheticEvent<ViroAmbientLightUpdateEvent>) => void;
58
+ /**
59
+ * Fired once when depth data first becomes available for hit testing.
60
+ */
61
+ _onDepthReady: (_event: NativeSyntheticEvent<{}>) => void;
50
62
  _onAnchorFound: (event: NativeSyntheticEvent<ViroARAnchorFoundEvent>) => void;
51
63
  _onAnchorUpdated: (event: NativeSyntheticEvent<ViroARAnchorUpdatedEvent>) => void;
52
64
  _onAnchorRemoved: (event: NativeSyntheticEvent<ViroARAnchorRemovedEvent>) => void;
@@ -176,6 +176,12 @@ class ViroARScene extends ViroBase_1.ViroBase {
176
176
  this.props.onAmbientLightUpdate &&
177
177
  this.props.onAmbientLightUpdate(event.nativeEvent.ambientLightInfo);
178
178
  };
179
+ /**
180
+ * Fired once when depth data first becomes available for hit testing.
181
+ */
182
+ _onDepthReady = (_event) => {
183
+ this.props.onDepthReady && this.props.onDepthReady();
184
+ };
179
185
  _onAnchorFound = (event) => {
180
186
  // TODO: this is in a different format than the other onAnchorFound methods
181
187
  this.props.onAnchorFound &&
@@ -334,7 +340,7 @@ class ViroARScene extends ViroBase_1.ViroBase {
334
340
  },
335
341
  }}>
336
342
  <VRTARScene {...this.props} canHover={this.props.onHover != undefined} canClick={this.props.onClick != undefined ||
337
- this.props.onClickState != undefined} canTouch={this.props.onTouch != undefined} canScroll={this.props.onScroll != undefined} canSwipe={this.props.onSwipe != undefined} canDrag={this.props.onDrag != undefined} canPinch={this.props.onPinch != undefined} canRotate={this.props.onRotate != undefined} canFuse={this.props.onFuse != undefined} canCameraARHitTest={this.props.onCameraARHitTest != undefined} canARPointCloudUpdate={this.props.onARPointCloudUpdate != undefined} canCameraTransformUpdate={this.props.onCameraTransformUpdate != undefined} onHoverViro={this._onHover} onClickViro={this._onClickState} onClick={undefined} onTouchViro={this._onTouch} onScrollViro={this._onScroll} onSwipeViro={this._onSwipe} onDragViro={this._onDrag} onPinchViro={this._onPinch} onRotateViro={this._onRotate} onFuseViro={this._onFuse} onCameraARHitTestViro={this._onCameraARHitTest} onARPointCloudUpdateViro={this._onARPointCloudUpdate} onCameraTransformUpdateViro={this._onCameraTransformUpdate} onPlatformUpdateViro={this._onPlatformUpdate} onTrackingUpdatedViro={this._onTrackingUpdated} onAmbientLightUpdateViro={this._onAmbientLightUpdate} onAnchorFoundViro={this._onAnchorFound} onAnchorUpdatedViro={this._onAnchorUpdated} onAnchorRemovedViro={this._onAnchorRemoved} timeToFuse={timeToFuse} anchorDetectionTypes={anchorDetectionTypes} displayPointCloud={displayPointCloud} pointCloudImage={pointCloudImage} pointCloudScale={pointCloudScale} pointCloudMaxPoints={pointCloudMaxPoints}/>
343
+ this.props.onClickState != undefined} canTouch={this.props.onTouch != undefined} canScroll={this.props.onScroll != undefined} canSwipe={this.props.onSwipe != undefined} canDrag={this.props.onDrag != undefined} canPinch={this.props.onPinch != undefined} canRotate={this.props.onRotate != undefined} canFuse={this.props.onFuse != undefined} canCameraARHitTest={this.props.onCameraARHitTest != undefined} canARPointCloudUpdate={this.props.onARPointCloudUpdate != undefined} canCameraTransformUpdate={this.props.onCameraTransformUpdate != undefined} onHoverViro={this._onHover} onClickViro={this._onClickState} onClick={undefined} onTouchViro={this._onTouch} onScrollViro={this._onScroll} onSwipeViro={this._onSwipe} onDragViro={this._onDrag} onPinchViro={this._onPinch} onRotateViro={this._onRotate} onFuseViro={this._onFuse} onCameraARHitTestViro={this._onCameraARHitTest} onARPointCloudUpdateViro={this._onARPointCloudUpdate} onCameraTransformUpdateViro={this._onCameraTransformUpdate} onPlatformUpdateViro={this._onPlatformUpdate} onTrackingUpdatedViro={this._onTrackingUpdated} onAmbientLightUpdateViro={this._onAmbientLightUpdate} onDepthReadyViro={this._onDepthReady} onAnchorFoundViro={this._onAnchorFound} onAnchorUpdatedViro={this._onAnchorUpdated} onAnchorRemovedViro={this._onAnchorRemoved} timeToFuse={timeToFuse} anchorDetectionTypes={anchorDetectionTypes} displayPointCloud={displayPointCloud} pointCloudImage={pointCloudImage} pointCloudScale={pointCloudScale} pointCloudMaxPoints={pointCloudMaxPoints}/>
338
344
  </ViroSceneContext_1.ViroSceneContext.Provider>);
339
345
  }
340
346
  }
@@ -368,6 +374,7 @@ ViroARScene, {
368
374
  onTrackingInitializedViro: true,
369
375
  onTrackingUpdatedViro: true,
370
376
  onAmbientLightUpdateViro: true,
377
+ onDepthReadyViro: true,
371
378
  onAnchorFoundViro: true,
372
379
  onAnchorUpdatedViro: true,
373
380
  onAnchorRemovedViro: true,
@@ -127,6 +127,36 @@ type Props = ViewProps & {
127
127
  * @platform ios
128
128
  */
129
129
  preferMonocularDepth?: boolean;
130
+ /**
131
+ * Calibration scale applied to monocular depth values before use in occlusion.
132
+ * 1.0 (default) = no change. Use < 1.0 if the model overestimates distances
133
+ * (virtual objects visible through real surfaces). Use > 1.0 if it underestimates.
134
+ * Typical tuning range: 0.7 – 1.3.
135
+ *
136
+ * @default 1.0
137
+ * @platform ios
138
+ */
139
+ monocularDepthScale?: number;
140
+ /**
141
+ * Maximum inference rate for monocular depth (default: 5).
142
+ * Lower values reduce device heat. Thermal state automatically
143
+ * overrides this downward: Fair→3fps, Serious→2fps, Critical→stopped.
144
+ * 3fps is barely perceptible for occlusion; 5fps is very smooth.
145
+ *
146
+ * @default 5
147
+ * @platform ios
148
+ */
149
+ monocularDepthTargetFPS?: number;
150
+ /**
151
+ * Use the front (selfie) camera as the AR session background.
152
+ * On iOS uses ARFaceTrackingConfiguration (requires TrueDepth camera, iPhone X+).
153
+ * On Android uses ARCore Augmented Faces mode (front camera).
154
+ * World tracking, plane detection, and LiDAR are unavailable in this mode.
155
+ *
156
+ * @default false
157
+ * @platform ios, android
158
+ */
159
+ frontCameraEnabled?: boolean;
130
160
  /**
131
161
  * Cloud and geospatial anchor provider.
132
162
  * Set to `"reactvision"` (default) for the ReactVision backend,
@@ -0,0 +1,22 @@
1
+ /**
2
+ * StreamingAudioManager — imperative API for streaming PCM audio.
3
+ *
4
+ * Typical TTS flow:
5
+ * StreamingAudioManager.create('tts');
6
+ * StreamingAudioManager.beginStreaming('tts', 24000, 1);
7
+ * StreamingAudioManager.play('tts');
8
+ * // on each TTS audio chunk:
9
+ * StreamingAudioManager.pushSamples('tts', base64FloatPCM);
10
+ * // when done:
11
+ * StreamingAudioManager.destroy('tts');
12
+ */
13
+ export declare const StreamingAudioManager: {
14
+ create: (id: string) => void;
15
+ beginStreaming: (id: string, sampleRate: number, channels: number) => void;
16
+ play: (id: string) => void;
17
+ pause: (id: string) => void;
18
+ setVolume: (id: string, volume: number) => void;
19
+ setMuted: (id: string, muted: boolean) => void;
20
+ pushSamples: (id: string, base64: string) => void;
21
+ destroy: (id: string) => void;
22
+ };
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.StreamingAudioManager = void 0;
4
+ const react_native_1 = require("react-native");
5
+ // RCT_EXPORT_MODULE() in newer RN (0.76+) keeps the "Module" suffix in NativeModules.
6
+ const native = (react_native_1.NativeModules.VRTStreamingAudioModule ?? react_native_1.NativeModules.VRTStreamingAudio);
7
+ function noop(..._args) { }
8
+ /**
9
+ * StreamingAudioManager — imperative API for streaming PCM audio.
10
+ *
11
+ * Typical TTS flow:
12
+ * StreamingAudioManager.create('tts');
13
+ * StreamingAudioManager.beginStreaming('tts', 24000, 1);
14
+ * StreamingAudioManager.play('tts');
15
+ * // on each TTS audio chunk:
16
+ * StreamingAudioManager.pushSamples('tts', base64FloatPCM);
17
+ * // when done:
18
+ * StreamingAudioManager.destroy('tts');
19
+ */
20
+ exports.StreamingAudioManager = {
21
+ create: native
22
+ ? (id) => native.create(id)
23
+ : noop,
24
+ beginStreaming: native
25
+ ? (id, sampleRate, channels) => native.beginStreaming(id, sampleRate, channels)
26
+ : noop,
27
+ play: native
28
+ ? (id) => native.play(id)
29
+ : noop,
30
+ pause: native
31
+ ? (id) => native.pause(id)
32
+ : noop,
33
+ setVolume: native
34
+ ? (id, volume) => native.setVolume(id, volume)
35
+ : noop,
36
+ setMuted: native
37
+ ? (id, muted) => native.setMuted(id, muted)
38
+ : noop,
39
+ pushSamples: native
40
+ ? (id, base64) => native.pushSamples(id, base64)
41
+ : noop,
42
+ destroy: native
43
+ ? (id) => native.destroy(id)
44
+ : noop,
45
+ };