@reactvision/react-viro 2.56.0 → 2.57.2
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/README.md +8 -0
- package/android/react_viro/react_viro-release.aar +0 -0
- package/android/viro_renderer/viro_renderer-release.aar +0 -0
- package/components/AR/ViroARPlane.tsx +1 -4
- package/components/AR/ViroARPlaneSelector.tsx +10 -5
- package/components/AR/ViroARScene.tsx +19 -4
- package/components/Studio/StudioARScene.tsx +243 -48
- package/components/Studio/StudioSceneNavigator.tsx +15 -0
- package/components/Studio/VRTStudioModule.ts +10 -0
- package/components/Studio/domain/StudioSounds.tsx +46 -0
- package/components/Studio/domain/apiRequestHelpers.ts +612 -0
- package/components/Studio/domain/collisionBindingsRuntime.ts +17 -2
- package/components/Studio/domain/defaultApiRequestExecutor.ts +61 -0
- package/components/Studio/domain/expressionEvaluator.ts +603 -0
- package/components/Studio/domain/sceneNavigationHandler.ts +770 -20
- package/components/Studio/domain/soundManager.ts +141 -0
- package/components/Studio/domain/utils.ts +50 -0
- package/components/Studio/domain/variableStore.ts +74 -0
- package/components/Studio/domain/viroNodeFactory.tsx +175 -44
- package/components/Studio/domain/visibilityStore.ts +66 -0
- package/components/Studio/index.ts +1 -0
- package/components/Studio/types.ts +178 -1
- package/components/Utilities/VRModuleOpenXR.ts +35 -0
- package/components/Utilities/ViroVersion.ts +1 -1
- package/components/ViroAnimatedImage.tsx +1 -0
- package/components/ViroFlexView.tsx +1 -0
- package/components/ViroImage.tsx +1 -0
- package/components/ViroObjectDetector.tsx +230 -0
- package/components/ViroParticleEmitter.tsx +2 -0
- package/components/ViroPolygon.tsx +2 -1
- package/components/ViroQuad.tsx +2 -1
- package/components/ViroSurface.tsx +1 -0
- package/components/ViroVideo.tsx +1 -0
- package/components/ViroXRSceneNavigator.tsx +8 -0
- package/dist/components/AR/ViroARPlane.d.ts +1 -1
- package/dist/components/AR/ViroARPlane.js +1 -5
- package/dist/components/AR/ViroARPlaneSelector.d.ts +1 -1
- package/dist/components/AR/ViroARPlaneSelector.js +10 -5
- package/dist/components/AR/ViroARScene.d.ts +13 -1
- package/dist/components/AR/ViroARScene.js +10 -6
- package/dist/components/Studio/StudioARScene.d.ts +3 -0
- package/dist/components/Studio/StudioARScene.js +142 -15
- package/dist/components/Studio/StudioSceneNavigator.js +14 -0
- package/dist/components/Studio/VRTStudioModule.d.ts +6 -0
- package/dist/components/Studio/VRTStudioModule.js +10 -0
- package/dist/components/Studio/domain/StudioSounds.d.ts +11 -0
- package/dist/components/Studio/domain/StudioSounds.js +69 -0
- package/dist/components/Studio/domain/apiRequestHelpers.d.ts +180 -0
- package/dist/components/Studio/domain/apiRequestHelpers.js +496 -0
- package/dist/components/Studio/domain/collisionBindingsRuntime.d.ts +3 -1
- package/dist/components/Studio/domain/collisionBindingsRuntime.js +4 -3
- package/dist/components/Studio/domain/defaultApiRequestExecutor.d.ts +14 -0
- package/dist/components/Studio/domain/defaultApiRequestExecutor.js +58 -0
- package/dist/components/Studio/domain/expressionEvaluator.d.ts +108 -0
- package/dist/components/Studio/domain/expressionEvaluator.js +520 -0
- package/dist/components/Studio/domain/sceneNavigationHandler.d.ts +35 -8
- package/dist/components/Studio/domain/sceneNavigationHandler.js +564 -22
- package/dist/components/Studio/domain/soundManager.d.ts +52 -0
- package/dist/components/Studio/domain/soundManager.js +118 -0
- package/dist/components/Studio/domain/utils.d.ts +16 -0
- package/dist/components/Studio/domain/utils.js +48 -0
- package/dist/components/Studio/domain/variableStore.d.ts +21 -0
- package/dist/components/Studio/domain/variableStore.js +65 -0
- package/dist/components/Studio/domain/viroNodeFactory.d.ts +4 -9
- package/dist/components/Studio/domain/viroNodeFactory.js +84 -25
- package/dist/components/Studio/domain/visibilityStore.d.ts +33 -0
- package/dist/components/Studio/domain/visibilityStore.js +66 -0
- package/dist/components/Studio/index.d.ts +1 -1
- package/dist/components/Studio/types.d.ts +150 -1
- package/dist/components/Utilities/VRModuleOpenXR.d.ts +19 -0
- package/dist/components/Utilities/VRModuleOpenXR.js +16 -0
- package/dist/components/Utilities/ViroVersion.d.ts +1 -1
- package/dist/components/Utilities/ViroVersion.js +1 -1
- package/dist/components/ViroAnimatedImage.js +1 -0
- package/dist/components/ViroFlexView.js +1 -0
- package/dist/components/ViroImage.js +1 -0
- package/dist/components/ViroObjectDetector.d.ts +146 -0
- package/dist/components/ViroObjectDetector.js +93 -0
- package/dist/components/ViroParticleEmitter.js +2 -0
- package/dist/components/ViroPolygon.js +1 -0
- package/dist/components/ViroQuad.js +1 -0
- package/dist/components/ViroSurface.js +1 -0
- package/dist/components/ViroVideo.js +1 -0
- package/dist/components/ViroXRSceneNavigator.d.ts +8 -0
- package/dist/index.d.ts +6 -4
- package/dist/index.js +5 -2
- package/dist/plugins/withViro.js +2 -15
- package/dist/plugins/withViroAndroid.js +18 -5
- package/dist/plugins/withViroIos.js +0 -6
- package/index.ts +7 -3
- package/ios/ViroReact.podspec +25 -6
- package/ios/dist/ViroRenderer/ViroKit.framework/ARCoreCoreMLSemanticsResources.bundle/Info.plist +0 -0
- package/ios/dist/ViroRenderer/ViroKit.framework/ARCoreResources.bundle/Info.plist +0 -0
- package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROARFrameiOS.h +9 -0
- package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROARScene.h +14 -1
- package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROARSceneDelegateiOS.h +8 -0
- package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROGLTFLoader.h +5 -1
- package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROMonocularDepthEstimator.h +10 -0
- package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROObjectDetector.h +134 -0
- package/ios/dist/ViroRenderer/ViroKit.framework/Info.plist +0 -0
- package/ios/dist/ViroRenderer/ViroKit.framework/Shaders.dat +1 -1
- package/ios/dist/ViroRenderer/ViroKit.framework/ViroKit +0 -0
- package/ios/dist/ViroRendererVisionOS/ViroKit.podspec +27 -0
- package/ios/dist/include/VRTARScene.h +1 -0
- package/ios/dist/lib/libViroReact.a +0 -0
- package/package.json +17 -20
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { StudioAsset } from "../types";
|
|
2
|
+
import { KeyedListeners, isDev } from "./utils";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Per-scene visibility store, keyed by scene asset placement id. Seeded from
|
|
6
|
+
* each asset's author-time `hidden_on_load` default at scene load; Set
|
|
7
|
+
* Visibility actions then flip the live value. Toggle reads the current value
|
|
8
|
+
* here, never the author default.
|
|
9
|
+
*
|
|
10
|
+
* Listeners are per-asset (unlike the global variable store) so a Set
|
|
11
|
+
* Visibility on one object repaints only that node, not the whole scene. Node
|
|
12
|
+
* factories subscribe to their own asset id.
|
|
13
|
+
*/
|
|
14
|
+
export class StudioVisibilityStore {
|
|
15
|
+
private visible = new Map<string, boolean>();
|
|
16
|
+
private listeners = new KeyedListeners();
|
|
17
|
+
|
|
18
|
+
/** Current visibility; defaults to visible for assets never seeded/set. */
|
|
19
|
+
isVisible(assetId: string): boolean {
|
|
20
|
+
return this.visible.get(assetId) ?? true;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Subscribe to changes for one asset; returns an unsubscribe fn. */
|
|
24
|
+
subscribe(assetId: string, listener: () => void): () => void {
|
|
25
|
+
return this.listeners.subscribe(assetId, listener);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Initialise-if-absent from author-time defaults (idempotent, strict-mode safe). */
|
|
29
|
+
seed(assets: StudioAsset[]): void {
|
|
30
|
+
for (const asset of assets) {
|
|
31
|
+
if (!asset?.id) continue;
|
|
32
|
+
if (this.visible.has(asset.id)) continue;
|
|
33
|
+
this.visible.set(asset.id, !asset.hidden_on_load);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Apply a Set Visibility action. TOGGLE flips the live value; VISIBLE/HIDDEN
|
|
39
|
+
* set it absolutely. No-op writes don't wake subscribers.
|
|
40
|
+
*/
|
|
41
|
+
apply(assetId: string, state: "VISIBLE" | "HIDDEN" | "TOGGLE"): void {
|
|
42
|
+
const next =
|
|
43
|
+
state === "TOGGLE" ? !this.isVisible(assetId) : state === "VISIBLE";
|
|
44
|
+
if (this.visible.get(assetId) === next) return;
|
|
45
|
+
this.visible.set(assetId, next);
|
|
46
|
+
if (isDev()) {
|
|
47
|
+
console.log(`[Studio] Visibility "${assetId}" =`, next);
|
|
48
|
+
}
|
|
49
|
+
this.listeners.notify(assetId);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Re-initialise for a new scene: replace all values from author-time
|
|
54
|
+
* defaults, THEN notify every subscribed asset so mounted nodes re-read.
|
|
55
|
+
* Order matters — notifying before re-seeding would briefly surface the
|
|
56
|
+
* visible default for an asset that starts hidden.
|
|
57
|
+
*/
|
|
58
|
+
reseed(assets: StudioAsset[]): void {
|
|
59
|
+
this.visible.clear();
|
|
60
|
+
for (const asset of assets) {
|
|
61
|
+
if (!asset?.id) continue;
|
|
62
|
+
this.visible.set(asset.id, !asset.hidden_on_load);
|
|
63
|
+
}
|
|
64
|
+
this.listeners.notifyAll();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -24,13 +24,159 @@ export interface StudioProjectMeta {
|
|
|
24
24
|
occlusion_mode: "NONE" | "PEOPLEONLY" | "DEPTHBASED";
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
/**
|
|
28
|
+
* One ordered step of a Sequence: an ACTION (runs a function), a WAIT (timed
|
|
29
|
+
* pause), or a STOP (explicit terminal — the run halts here).
|
|
30
|
+
*/
|
|
31
|
+
export interface StudioSequenceStep {
|
|
32
|
+
id: string;
|
|
33
|
+
step_order: number;
|
|
34
|
+
step_type: "ACTION" | "WAIT" | "STOP";
|
|
35
|
+
duration_ms: number | null; // set for WAIT
|
|
36
|
+
function_id: string | null; // set for ACTION
|
|
37
|
+
function: StudioSceneFunction | null; // resolved child for ACTION; null for WAIT/STOP
|
|
38
|
+
// false (default) = wait for this step's effect to finish before the next
|
|
39
|
+
// step; true = fire-and-forget. Ignored for instant/terminal kinds.
|
|
40
|
+
advance_immediately: boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface StudioSequence {
|
|
44
|
+
id: string;
|
|
45
|
+
name: string | null;
|
|
46
|
+
steps: StudioSequenceStep[];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Scene-level variable declaration. Runtime state lives in StudioVariableStore. */
|
|
50
|
+
export interface StudioSceneVariable {
|
|
51
|
+
id: string;
|
|
52
|
+
name: string;
|
|
53
|
+
type: "BOOLEAN" | "NUMBER" | "STRING";
|
|
54
|
+
initial_value: boolean | number | string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* One ordered predicate arm of a BRANCH. Variable names/types are joined from
|
|
59
|
+
* project_variables so they follow renames. The arm is an owned, headless
|
|
60
|
+
* sequence run like a nested sequence.
|
|
61
|
+
*/
|
|
62
|
+
export interface StudioBranchCondition {
|
|
63
|
+
id: string;
|
|
64
|
+
eval_order: number;
|
|
65
|
+
variable_id: string;
|
|
66
|
+
variable_name: string;
|
|
67
|
+
variable_type: "BOOLEAN" | "NUMBER" | "STRING";
|
|
68
|
+
comparison:
|
|
69
|
+
| "EQUALS"
|
|
70
|
+
| "NOT_EQUALS"
|
|
71
|
+
| "GREATER_THAN"
|
|
72
|
+
| "LESS_THAN"
|
|
73
|
+
| "GREATER_OR_EQUAL"
|
|
74
|
+
| "LESS_OR_EQUAL"
|
|
75
|
+
| "LIKE"
|
|
76
|
+
| "ILIKE";
|
|
77
|
+
compare_literal: boolean | number | string | null;
|
|
78
|
+
compare_variable_id: string | null;
|
|
79
|
+
compare_variable_name: string | null;
|
|
80
|
+
compare_variable_type: "BOOLEAN" | "NUMBER" | "STRING" | null;
|
|
81
|
+
sequence: StudioSequence;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* BRANCH payload. Conditions are evaluated in eval_order (first match wins),
|
|
86
|
+
* falling through to the optional no-match arm. Arms are owned, headless
|
|
87
|
+
* sequences run like nested sequences.
|
|
88
|
+
*/
|
|
89
|
+
export interface StudioSceneBranch {
|
|
90
|
+
id: string;
|
|
91
|
+
conditions: StudioBranchCondition[];
|
|
92
|
+
no_match_sequence: StudioSequence | null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* GROUP payload. Owns N lanes, each an owned headless sequence run like a
|
|
97
|
+
* nested sequence. Lanes run concurrently; the group completes when all lanes
|
|
98
|
+
* complete (barrier join).
|
|
99
|
+
*/
|
|
100
|
+
export interface StudioSceneGroup {
|
|
101
|
+
id: string;
|
|
102
|
+
lanes: { lane_order: number; sequence: StudioSequence }[];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** One response->variable binding of an API_REQUEST function. */
|
|
106
|
+
export interface StudioApiRequestBinding {
|
|
107
|
+
id: string;
|
|
108
|
+
source: "BODY" | "STATUS" | "OK" | "ERROR_MESSAGE";
|
|
109
|
+
selector: string | null;
|
|
110
|
+
variable_id: string;
|
|
111
|
+
variable_name: string;
|
|
112
|
+
variable_type: "BOOLEAN" | "NUMBER" | "STRING";
|
|
113
|
+
bind_order: number;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* API_REQUEST payload. The HTTP call executes server-side through the
|
|
118
|
+
* platform egress proxy (the client only ever sends the function id plus
|
|
119
|
+
* current variable values); connection_id is an opaque server-side
|
|
120
|
+
* reference. Arms are owned, headless sequences run like nested sequences.
|
|
121
|
+
*/
|
|
122
|
+
export interface StudioSceneApiRequest {
|
|
123
|
+
id: string;
|
|
124
|
+
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
125
|
+
url_template: string;
|
|
126
|
+
headers: { key: string; value_template: string }[];
|
|
127
|
+
body_template: string | null;
|
|
128
|
+
timeout_ms: number;
|
|
129
|
+
connection_id: string | null;
|
|
130
|
+
bindings: StudioApiRequestBinding[];
|
|
131
|
+
success_sequence: StudioSequence | null;
|
|
132
|
+
failure_sequence: StudioSequence | null;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** Outcome envelope an API-request executor resolves with. */
|
|
136
|
+
export interface StudioApiRequestOutcome {
|
|
137
|
+
ok: boolean;
|
|
138
|
+
status: number | null;
|
|
139
|
+
body?: unknown;
|
|
140
|
+
error_code?: string | null;
|
|
141
|
+
error_message?: string | null;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Host-app transport for API_REQUEST functions: StudioGo calls the
|
|
146
|
+
* scene-api-request edge function with the user JWT; production Viro apps
|
|
147
|
+
* call it with their X-API-Key. Must resolve with an outcome (reject only on
|
|
148
|
+
* transport failure).
|
|
149
|
+
*/
|
|
150
|
+
export type StudioApiRequestExecutor = (
|
|
151
|
+
functionId: string,
|
|
152
|
+
variables: Record<string, boolean | number | string>,
|
|
153
|
+
) => Promise<StudioApiRequestOutcome>;
|
|
154
|
+
|
|
27
155
|
export interface StudioSceneFunction {
|
|
28
156
|
id: string;
|
|
29
157
|
scene: string;
|
|
30
|
-
function_type:
|
|
158
|
+
function_type:
|
|
159
|
+
| "NAVIGATION"
|
|
160
|
+
| "ALERT"
|
|
161
|
+
| "ANIMATION"
|
|
162
|
+
| "SEQUENCE"
|
|
163
|
+
| "SET_VARIABLE"
|
|
164
|
+
| "BRANCH"
|
|
165
|
+
| "API_REQUEST"
|
|
166
|
+
| "SET_VISIBILITY"
|
|
167
|
+
| "SOUND"
|
|
168
|
+
| "GROUP";
|
|
31
169
|
navigation: string | null;
|
|
32
170
|
alert: string | null;
|
|
33
171
|
animation: string | null;
|
|
172
|
+
sequence: string | null;
|
|
173
|
+
set_variable: string | null;
|
|
174
|
+
branch: string | null;
|
|
175
|
+
api_request: string | null;
|
|
176
|
+
set_visibility: string | null;
|
|
177
|
+
sound: string | null;
|
|
178
|
+
// Named group_fn because `group` is a reserved SQL keyword.
|
|
179
|
+
group_fn: string | null;
|
|
34
180
|
scene_navigation: { id: string; navigate_to: string } | null;
|
|
35
181
|
scene_alert: {
|
|
36
182
|
id: string;
|
|
@@ -44,6 +190,33 @@ export interface StudioSceneFunction {
|
|
|
44
190
|
delay_ms: number | null;
|
|
45
191
|
properties: Record<string, unknown>;
|
|
46
192
|
} | null;
|
|
193
|
+
scene_sequence: StudioSequence | null;
|
|
194
|
+
/** name/type are joined from the target scene_variables row, so they follow renames. */
|
|
195
|
+
scene_set_variable: {
|
|
196
|
+
id: string;
|
|
197
|
+
variable_id: string;
|
|
198
|
+
name: string;
|
|
199
|
+
type: "BOOLEAN" | "NUMBER" | "STRING";
|
|
200
|
+
expression: string;
|
|
201
|
+
} | null;
|
|
202
|
+
scene_branch: StudioSceneBranch | null;
|
|
203
|
+
scene_api_request: StudioSceneApiRequest | null;
|
|
204
|
+
scene_set_visibility: {
|
|
205
|
+
id: string;
|
|
206
|
+
target_asset_id: string;
|
|
207
|
+
state: "VISIBLE" | "HIDDEN" | "TOGGLE";
|
|
208
|
+
} | null;
|
|
209
|
+
scene_sound: {
|
|
210
|
+
id: string;
|
|
211
|
+
action: "PLAY" | "STOP";
|
|
212
|
+
audio_asset_id: string | null;
|
|
213
|
+
audio_url: string | null;
|
|
214
|
+
target_asset_id: string | null;
|
|
215
|
+
volume: number;
|
|
216
|
+
loop: boolean;
|
|
217
|
+
stop_other_sounds: boolean;
|
|
218
|
+
} | null;
|
|
219
|
+
scene_group: StudioSceneGroup | null;
|
|
47
220
|
}
|
|
48
221
|
|
|
49
222
|
export interface StudioAsset {
|
|
@@ -63,6 +236,8 @@ export interface StudioAsset {
|
|
|
63
236
|
latitude: number | null;
|
|
64
237
|
longitude: number | null;
|
|
65
238
|
is_draggable: boolean;
|
|
239
|
+
/** Author-time "hidden on start"; the runtime seeds visibility from it. */
|
|
240
|
+
hidden_on_load: boolean | null;
|
|
66
241
|
trigger_image_url: string | null;
|
|
67
242
|
trigger_image_orientation: "Up" | "Down" | "Left" | "Right" | null;
|
|
68
243
|
trigger_image_physical_width_m: number | null;
|
|
@@ -149,6 +324,8 @@ export interface StudioSceneResponse {
|
|
|
149
324
|
collision_bindings: StudioCollisionBinding[];
|
|
150
325
|
animations: StudioAnimation[];
|
|
151
326
|
functions: StudioSceneFunction[];
|
|
327
|
+
/** Absent in responses from backends predating the Variables feature. */
|
|
328
|
+
variables?: StudioSceneVariable[];
|
|
152
329
|
meta: { request_id: string };
|
|
153
330
|
}
|
|
154
331
|
|
|
@@ -15,8 +15,43 @@ export function exitVRScene(): void {
|
|
|
15
15
|
export type VRModuleOpenXRType = {
|
|
16
16
|
recenterTracking?: (viewTag: number) => void;
|
|
17
17
|
setPassthroughEnabled?: (viewTag: number, enabled: boolean) => void;
|
|
18
|
+
setPassthroughStyle?: (
|
|
19
|
+
viewTag: number,
|
|
20
|
+
opacity: number,
|
|
21
|
+
edgeR: number,
|
|
22
|
+
edgeG: number,
|
|
23
|
+
edgeB: number,
|
|
24
|
+
edgeA: number
|
|
25
|
+
) => void;
|
|
18
26
|
};
|
|
19
27
|
|
|
28
|
+
/** Options for {@link setPassthroughStyle}. All channels are normalized [0,1]. */
|
|
29
|
+
export type ViroPassthroughStyle = {
|
|
30
|
+
/** Texture opacity factor [0,1]. Default 1 (fully opaque passthrough). */
|
|
31
|
+
opacity?: number;
|
|
32
|
+
/** Edge-highlight colour [r,g,b,a]. Alpha 0 (default) disables the edge effect. */
|
|
33
|
+
edgeColor?: [number, number, number, number];
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Style the Quest passthrough layer (XR_FB_passthrough). No-op off-Quest.
|
|
38
|
+
*
|
|
39
|
+
* ```tsx
|
|
40
|
+
* const viewTag = useVRViewTag();
|
|
41
|
+
* if (viewTag != null) {
|
|
42
|
+
* setPassthroughStyle(viewTag, { opacity: 0.8, edgeColor: [0, 1, 1, 1] });
|
|
43
|
+
* }
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export function setPassthroughStyle(
|
|
47
|
+
viewTag: number,
|
|
48
|
+
style: ViroPassthroughStyle
|
|
49
|
+
): void {
|
|
50
|
+
const opacity = style.opacity ?? 1;
|
|
51
|
+
const [r, g, b, a] = style.edgeColor ?? [0, 0, 0, 0];
|
|
52
|
+
VRModuleOpenXR?.setPassthroughStyle?.(viewTag, opacity, r, g, b, a);
|
|
53
|
+
}
|
|
54
|
+
|
|
20
55
|
/**
|
|
21
56
|
* Typed reference to the VRModuleOpenXR native module.
|
|
22
57
|
* undefined when not running on Meta Quest (no-op calls are safe via optional chaining).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VIRO_VERSION = "2.
|
|
1
|
+
export const VIRO_VERSION = "2.57.2";
|
|
@@ -170,6 +170,7 @@ export class ViroAnimatedImage extends ViroBase<Props> {
|
|
|
170
170
|
nativeProps.style = [this.props.style];
|
|
171
171
|
nativeProps.onHoverViro = this._onHover;
|
|
172
172
|
nativeProps.onClickViro = this._onClickState;
|
|
173
|
+
nativeProps.onClick = undefined;
|
|
173
174
|
nativeProps.onTouchViro = this._onTouch;
|
|
174
175
|
nativeProps.onScrollViro = this._onScroll;
|
|
175
176
|
nativeProps.onSwipeViro = this._onSwipe;
|
|
@@ -59,6 +59,7 @@ export class ViroFlexView extends ViroBase<Props> {
|
|
|
59
59
|
nativeProps.transformBehaviors = transformBehaviors;
|
|
60
60
|
nativeProps.onHoverViro = this._onHover;
|
|
61
61
|
nativeProps.onClickViro = this._onClickState;
|
|
62
|
+
nativeProps.onClick = undefined;
|
|
62
63
|
nativeProps.onTouchViro = this._onTouch;
|
|
63
64
|
nativeProps.onScrollViro = this._onScroll;
|
|
64
65
|
nativeProps.onSwipeViro = this._onSwipe;
|
package/components/ViroImage.tsx
CHANGED
|
@@ -142,6 +142,7 @@ export class ViroImage extends ViroBase<Props> {
|
|
|
142
142
|
nativeProps.style = [this.props.style];
|
|
143
143
|
nativeProps.onHoverViro = this._onHover;
|
|
144
144
|
nativeProps.onClickViro = this._onClickState;
|
|
145
|
+
nativeProps.onClick = undefined;
|
|
145
146
|
nativeProps.onTouchViro = this._onTouch;
|
|
146
147
|
nativeProps.onScrollViro = this._onScroll;
|
|
147
148
|
nativeProps.onSwipeViro = this._onSwipe;
|
|
@@ -0,0 +1,230 @@
|
|
|
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 in AR and on Meta Quest:
|
|
150
|
+
* - **Phone AR**: shares the camera feed of the enclosing `ViroARSceneNavigator`
|
|
151
|
+
* (no separate camera session) and fires `onDetection` with labels, normalized
|
|
152
|
+
* bounding boxes, and an on-screen `screenBoundingBox` (dp) at up to `maxFPS`.
|
|
153
|
+
* - **Meta Quest 3 / 3S** (Horizon OS v74+): when mounted in a Quest MR scene
|
|
154
|
+
* (under `ViroXRSceneNavigator`), it captures the headset camera via the Meta
|
|
155
|
+
* Passthrough Camera API and runs the same YOLOE pipeline. Requires the
|
|
156
|
+
* `horizonos.permission.HEADSET_CAMERA` runtime permission. v1 emits `label` +
|
|
157
|
+
* normalized `boundingBox` only — `screenBoundingBox` and `worldPosition` are
|
|
158
|
+
* not provided on Quest yet (they need camera extrinsics).
|
|
159
|
+
*
|
|
160
|
+
* Mount it as a child or sibling of the scene navigator; it renders nothing itself,
|
|
161
|
+
* so give it `width: 0, height: 0`.
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* ```tsx
|
|
165
|
+
* <ViroARSceneNavigator initialScene={{ scene: MyScene }} />
|
|
166
|
+
* <ViroObjectDetector
|
|
167
|
+
* style={{ position: "absolute", width: 0, height: 0 }}
|
|
168
|
+
* mode="prompt-free"
|
|
169
|
+
* confidenceThreshold={0.4}
|
|
170
|
+
* maxFPS={15}
|
|
171
|
+
* onDetection={({ detections }) => {
|
|
172
|
+
* detections.forEach(d => console.log(d.label, d.confidence, d.screenBoundingBox));
|
|
173
|
+
* }}
|
|
174
|
+
* />
|
|
175
|
+
* ```
|
|
176
|
+
*/
|
|
177
|
+
export const ViroObjectDetector: React.FC<Props> = ({
|
|
178
|
+
model = "yoloe-26s",
|
|
179
|
+
mode = "prompt-free",
|
|
180
|
+
categories = [],
|
|
181
|
+
confidenceThreshold = 0.4,
|
|
182
|
+
iouThreshold = 0.45,
|
|
183
|
+
maxFPS = 15,
|
|
184
|
+
maxDetections = 20,
|
|
185
|
+
projectToWorld = true,
|
|
186
|
+
onDetection,
|
|
187
|
+
onReady,
|
|
188
|
+
onError,
|
|
189
|
+
style,
|
|
190
|
+
...rest
|
|
191
|
+
}) => {
|
|
192
|
+
const handleDetection = React.useCallback(
|
|
193
|
+
(event: NativeDetectionEvent) => {
|
|
194
|
+
onDetection?.(event.nativeEvent);
|
|
195
|
+
},
|
|
196
|
+
[onDetection]
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
const handleReady = React.useCallback(
|
|
200
|
+
(event: NativeReadyEvent) => {
|
|
201
|
+
onReady?.(event.nativeEvent);
|
|
202
|
+
},
|
|
203
|
+
[onReady]
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
const handleError = React.useCallback(
|
|
207
|
+
(event: NativeErrorEvent) => {
|
|
208
|
+
onError?.(event.nativeEvent);
|
|
209
|
+
},
|
|
210
|
+
[onError]
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
return (
|
|
214
|
+
<VRTObjectDetectorView
|
|
215
|
+
{...rest}
|
|
216
|
+
style={style}
|
|
217
|
+
model={model}
|
|
218
|
+
mode={mode}
|
|
219
|
+
categories={categories}
|
|
220
|
+
confidenceThreshold={confidenceThreshold}
|
|
221
|
+
iouThreshold={iouThreshold}
|
|
222
|
+
maxFPS={maxFPS}
|
|
223
|
+
maxDetections={maxDetections}
|
|
224
|
+
projectToWorld={projectToWorld}
|
|
225
|
+
onDetectionViro={onDetection ? handleDetection : undefined}
|
|
226
|
+
onReadyViro={onReady ? handleReady : undefined}
|
|
227
|
+
onErrorViro={onError ? handleError : undefined}
|
|
228
|
+
/>
|
|
229
|
+
);
|
|
230
|
+
};
|
|
@@ -261,6 +261,7 @@ export class ViroParticleEmitter extends React.Component<Props, State> {
|
|
|
261
261
|
: undefined;
|
|
262
262
|
// Create native props object.
|
|
263
263
|
let nativeProps = Object.assign({} as any, this.props);
|
|
264
|
+
nativeProps.onClick = undefined;
|
|
264
265
|
nativeProps.position = this.state.propsPositionState;
|
|
265
266
|
nativeProps.onNativeTransformDelegateViro = transformDelegate;
|
|
266
267
|
nativeProps.hasTransformDelegate =
|
|
@@ -344,6 +345,7 @@ export class ViroParticleEmitter extends React.Component<Props, State> {
|
|
|
344
345
|
};
|
|
345
346
|
nativeProps.particleAppearance.rotation = newAppearanceRotMod;
|
|
346
347
|
}
|
|
348
|
+
nativeProps.onClick = undefined;
|
|
347
349
|
nativeProps.ref = (component: ViroNativeRef) => {
|
|
348
350
|
this._component = component;
|
|
349
351
|
};
|
|
@@ -60,7 +60,7 @@ type Props = {
|
|
|
60
60
|
* that texture partially over the entire surface.
|
|
61
61
|
*/
|
|
62
62
|
uvCoordinates?: ViroUVCoordinate;
|
|
63
|
-
arShadowReceiver?: boolean
|
|
63
|
+
arShadowReceiver?: boolean;
|
|
64
64
|
style?: ViroStyle;
|
|
65
65
|
};
|
|
66
66
|
|
|
@@ -104,6 +104,7 @@ export class ViroPolygon extends ViroBase<Props> {
|
|
|
104
104
|
nativeProps.style = [this.props.style];
|
|
105
105
|
nativeProps.onHoverViro = this._onHover;
|
|
106
106
|
nativeProps.onClickViro = this._onClickState;
|
|
107
|
+
nativeProps.onClick = undefined;
|
|
107
108
|
nativeProps.onTouchViro = this._onTouch;
|
|
108
109
|
nativeProps.onScrollViro = this._onScroll;
|
|
109
110
|
nativeProps.onSwipeViro = this._onSwipe;
|
package/components/ViroQuad.tsx
CHANGED
|
@@ -18,7 +18,7 @@ import { checkMisnamedProps } from "./Utilities/ViroProps";
|
|
|
18
18
|
import { ViroBase } from "./ViroBase";
|
|
19
19
|
|
|
20
20
|
type Props = {
|
|
21
|
-
arShadowReceiver?: boolean
|
|
21
|
+
arShadowReceiver?: boolean;
|
|
22
22
|
uvCoordinates?: ViroUVCoordinate[];
|
|
23
23
|
};
|
|
24
24
|
|
|
@@ -62,6 +62,7 @@ export class ViroQuad extends ViroBase<Props> {
|
|
|
62
62
|
nativeProps.style = [this.props.style];
|
|
63
63
|
nativeProps.onHoverViro = this._onHover;
|
|
64
64
|
nativeProps.onClickViro = this._onClickState;
|
|
65
|
+
nativeProps.onClick = undefined;
|
|
65
66
|
nativeProps.onTouchViro = this._onTouch;
|
|
66
67
|
nativeProps.onScrollViro = this._onScroll;
|
|
67
68
|
nativeProps.onSwipeViro = this._onSwipe;
|
|
@@ -65,6 +65,7 @@ export class ViroSurface extends ViroBase<Props> {
|
|
|
65
65
|
nativeProps.style = [this.props.style];
|
|
66
66
|
nativeProps.onHoverViro = this._onHover;
|
|
67
67
|
nativeProps.onClickViro = this._onClickState;
|
|
68
|
+
nativeProps.onClick = undefined;
|
|
68
69
|
nativeProps.onTouchViro = this._onTouch;
|
|
69
70
|
nativeProps.onScrollViro = this._onScroll;
|
|
70
71
|
nativeProps.onSwipeViro = this._onSwipe;
|
package/components/ViroVideo.tsx
CHANGED
|
@@ -131,6 +131,7 @@ export class ViroVideo extends ViroBase<Props> {
|
|
|
131
131
|
nativeProps.onUpdateTimeViro = this._onUpdateTime;
|
|
132
132
|
nativeProps.onHoverViro = this._onHover;
|
|
133
133
|
nativeProps.onClickViro = this._onClickState;
|
|
134
|
+
nativeProps.onClick = undefined;
|
|
134
135
|
nativeProps.onTouchViro = this._onTouch;
|
|
135
136
|
nativeProps.onScrollViro = this._onScroll;
|
|
136
137
|
nativeProps.onSwipeViro = this._onSwipe;
|
|
@@ -59,6 +59,14 @@ type Props = ViewProps & {
|
|
|
59
59
|
* On Quest, this scene is forwarded to VRActivity via VRQuestNavigatorBridge
|
|
60
60
|
* rather than rendered inline, because OpenXR exclusive display requires the
|
|
61
61
|
* VR intent category on the host Activity.
|
|
62
|
+
*
|
|
63
|
+
* The scene root may be either:
|
|
64
|
+
* - `ViroScene` → fully-virtual VR.
|
|
65
|
+
* - `ViroARScene` → mixed reality: passthrough is enabled automatically and
|
|
66
|
+
* real-time plane detection (XR_EXT_plane_detection, Quest 3 / 3S) drives
|
|
67
|
+
* `onAnchorFound` and `ViroARPlane`, exactly as on phone AR. A single
|
|
68
|
+
* AR-rooted `initialScene` therefore runs on phones (ARCore) and Quest (OpenXR)
|
|
69
|
+
* with no per-platform changes. See docs/QUEST_SETUP.md §7b.
|
|
62
70
|
*/
|
|
63
71
|
vrInitialScene?: SceneFactory;
|
|
64
72
|
|
|
@@ -25,6 +25,6 @@ export declare class ViroARPlane extends ViroBase<Props> {
|
|
|
25
25
|
_onAnchorFound: (event: NativeSyntheticEvent<ViroARAnchorFoundEvent>) => void;
|
|
26
26
|
_onAnchorUpdated: (event: NativeSyntheticEvent<ViroARAnchorUpdatedEvent>) => void;
|
|
27
27
|
_onAnchorRemoved: (_event: NativeSyntheticEvent<ViroARAnchorRemovedEvent>) => void;
|
|
28
|
-
render(): React.JSX.Element
|
|
28
|
+
render(): React.JSX.Element;
|
|
29
29
|
}
|
|
30
30
|
export {};
|