@reactvision/react-viro 2.54.0 → 2.55.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 (133) hide show
  1. package/README.md +85 -46
  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/ViroARCamera.tsx +5 -0
  5. package/components/AR/ViroARImageMarker.tsx +5 -0
  6. package/components/AR/ViroARObjectMarker.tsx +5 -0
  7. package/components/AR/ViroARPlane.tsx +5 -0
  8. package/components/AR/ViroARPlaneSelector.tsx +5 -0
  9. package/components/AR/ViroARScene.tsx +5 -0
  10. package/components/AR/ViroARSceneNavigator.tsx +54 -0
  11. package/components/Studio/StudioARScene.tsx +368 -0
  12. package/components/Studio/StudioSceneNavigator.tsx +191 -0
  13. package/components/Studio/VRTStudioModule.ts +40 -0
  14. package/components/Studio/domain/animationRegistry.ts +86 -0
  15. package/components/Studio/domain/collisionBindingsRuntime.ts +93 -0
  16. package/components/Studio/domain/collisionPairKey.ts +15 -0
  17. package/components/Studio/domain/dragConfiguration.ts +48 -0
  18. package/components/Studio/domain/materialConfig.ts +276 -0
  19. package/components/Studio/domain/physicsConfig.ts +204 -0
  20. package/components/Studio/domain/sceneNavigationHandler.ts +150 -0
  21. package/components/Studio/domain/studioMaterials.ts +33 -0
  22. package/components/Studio/domain/triggerImageRegistry.ts +64 -0
  23. package/components/Studio/domain/useStudioShaderTimeUniforms.ts +51 -0
  24. package/components/Studio/domain/useStudioShaderViewportUniforms.ts +52 -0
  25. package/components/Studio/domain/viroNodeFactory.tsx +323 -0
  26. package/components/Studio/index.ts +18 -0
  27. package/components/Studio/types.ts +164 -0
  28. package/components/Types/ViroEvents.ts +53 -0
  29. package/components/Utilities/VRModuleOpenXR.ts +50 -0
  30. package/components/Utilities/VRQuestNavigatorBridge.ts +168 -0
  31. package/components/Utilities/ViroPlatform.ts +52 -0
  32. package/components/Utilities/ViroVersion.ts +1 -1
  33. package/components/Utilities/useAnySourceHover.ts +55 -0
  34. package/components/Utilities/useAnySourcePressed.ts +70 -0
  35. package/components/ViroQuestEntryPoint.tsx +79 -0
  36. package/components/ViroVRSceneNavigator.tsx +44 -19
  37. package/components/ViroXRSceneNavigator.tsx +217 -0
  38. package/components/VisionOS/ViroVisionOSModule.ts +93 -0
  39. package/dist/components/AR/ViroARCamera.d.ts +1 -1
  40. package/dist/components/AR/ViroARCamera.js +5 -0
  41. package/dist/components/AR/ViroARImageMarker.d.ts +1 -1
  42. package/dist/components/AR/ViroARImageMarker.js +5 -0
  43. package/dist/components/AR/ViroARObjectMarker.d.ts +1 -1
  44. package/dist/components/AR/ViroARObjectMarker.js +5 -0
  45. package/dist/components/AR/ViroARPlane.d.ts +1 -1
  46. package/dist/components/AR/ViroARPlane.js +5 -0
  47. package/dist/components/AR/ViroARPlaneSelector.d.ts +1 -1
  48. package/dist/components/AR/ViroARPlaneSelector.js +5 -0
  49. package/dist/components/AR/ViroARScene.d.ts +1 -1
  50. package/dist/components/AR/ViroARScene.js +5 -0
  51. package/dist/components/AR/ViroARSceneNavigator.d.ts +13 -0
  52. package/dist/components/AR/ViroARSceneNavigator.js +36 -0
  53. package/dist/components/Studio/StudioARScene.d.ts +15 -0
  54. package/dist/components/Studio/StudioARScene.js +299 -0
  55. package/dist/components/Studio/StudioSceneNavigator.d.ts +31 -0
  56. package/dist/components/Studio/StudioSceneNavigator.js +174 -0
  57. package/dist/components/Studio/VRTStudioModule.d.ts +15 -0
  58. package/dist/components/Studio/VRTStudioModule.js +31 -0
  59. package/dist/components/Studio/domain/animationRegistry.d.ts +11 -0
  60. package/dist/components/Studio/domain/animationRegistry.js +67 -0
  61. package/dist/components/Studio/domain/collisionBindingsRuntime.d.ts +21 -0
  62. package/dist/components/Studio/domain/collisionBindingsRuntime.js +54 -0
  63. package/dist/components/Studio/domain/collisionPairKey.d.ts +8 -0
  64. package/dist/components/Studio/domain/collisionPairKey.js +15 -0
  65. package/dist/components/Studio/domain/dragConfiguration.d.ts +20 -0
  66. package/dist/components/Studio/domain/dragConfiguration.js +37 -0
  67. package/dist/components/Studio/domain/materialConfig.d.ts +56 -0
  68. package/dist/components/Studio/domain/materialConfig.js +239 -0
  69. package/dist/components/Studio/domain/physicsConfig.d.ts +69 -0
  70. package/dist/components/Studio/domain/physicsConfig.js +165 -0
  71. package/dist/components/Studio/domain/sceneNavigationHandler.d.ts +12 -0
  72. package/dist/components/Studio/domain/sceneNavigationHandler.js +112 -0
  73. package/dist/components/Studio/domain/studioMaterials.d.ts +6 -0
  74. package/dist/components/Studio/domain/studioMaterials.js +30 -0
  75. package/dist/components/Studio/domain/triggerImageRegistry.d.ts +13 -0
  76. package/dist/components/Studio/domain/triggerImageRegistry.js +47 -0
  77. package/dist/components/Studio/domain/useStudioShaderTimeUniforms.d.ts +6 -0
  78. package/dist/components/Studio/domain/useStudioShaderTimeUniforms.js +48 -0
  79. package/dist/components/Studio/domain/useStudioShaderViewportUniforms.d.ts +6 -0
  80. package/dist/components/Studio/domain/useStudioShaderViewportUniforms.js +48 -0
  81. package/dist/components/Studio/domain/viroNodeFactory.d.ts +28 -0
  82. package/dist/components/Studio/domain/viroNodeFactory.js +193 -0
  83. package/dist/components/Studio/index.d.ts +3 -0
  84. package/dist/components/Studio/index.js +7 -0
  85. package/dist/components/Studio/types.d.ts +149 -0
  86. package/dist/components/Studio/types.js +4 -0
  87. package/dist/components/Types/ViroEvents.d.ts +49 -1
  88. package/dist/components/Types/ViroEvents.js +1 -0
  89. package/dist/components/Utilities/VRModuleOpenXR.d.ts +32 -0
  90. package/dist/components/Utilities/VRModuleOpenXR.js +44 -0
  91. package/dist/components/Utilities/VRQuestNavigatorBridge.d.ts +85 -0
  92. package/dist/components/Utilities/VRQuestNavigatorBridge.js +124 -0
  93. package/dist/components/Utilities/ViroPlatform.d.ts +10 -0
  94. package/dist/components/Utilities/ViroPlatform.js +43 -0
  95. package/dist/components/Utilities/ViroVersion.d.ts +1 -1
  96. package/dist/components/Utilities/ViroVersion.js +1 -1
  97. package/dist/components/Utilities/useAnySourceHover.d.ts +36 -0
  98. package/dist/components/Utilities/useAnySourceHover.js +48 -0
  99. package/dist/components/Utilities/useAnySourcePressed.d.ts +37 -0
  100. package/dist/components/Utilities/useAnySourcePressed.js +61 -0
  101. package/dist/components/ViroQuestEntryPoint.d.ts +13 -0
  102. package/dist/components/ViroQuestEntryPoint.js +104 -0
  103. package/dist/components/ViroVRSceneNavigator.d.ts +24 -10
  104. package/dist/components/ViroVRSceneNavigator.js +21 -18
  105. package/dist/components/ViroXRSceneNavigator.d.ts +54 -0
  106. package/dist/components/ViroXRSceneNavigator.js +173 -0
  107. package/dist/components/VisionOS/ViroVisionOSModule.d.ts +65 -0
  108. package/dist/components/VisionOS/ViroVisionOSModule.js +91 -0
  109. package/dist/index.d.ts +15 -2
  110. package/dist/index.js +32 -2
  111. package/dist/plugins/withViro.d.ts +17 -1
  112. package/dist/plugins/withViroAndroid.js +312 -7
  113. package/dist/plugins/withViroIos.js +5 -0
  114. package/dist/plugins/withViroVisionOS.d.ts +24 -0
  115. package/dist/plugins/withViroVisionOS.js +265 -0
  116. package/index.ts +58 -0
  117. package/ios/ViroReact.podspec +13 -4
  118. package/ios/dist/ViroRenderer/ViroKit.framework/ARCoreCoreMLSemanticsResources.bundle/Info.plist +0 -0
  119. package/ios/dist/ViroRenderer/ViroKit.framework/ARCoreResources.bundle/Info.plist +0 -0
  120. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROARSession.h +10 -0
  121. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROARSessioniOS.h +4 -0
  122. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROInputControllerBase.h +74 -0
  123. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROInputType.h +11 -3
  124. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROPlatformUtil.h +13 -0
  125. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VRORenderer.h +8 -0
  126. package/ios/dist/ViroRenderer/ViroKit.framework/Info.plist +0 -0
  127. package/ios/dist/ViroRenderer/ViroKit.framework/Shaders.dat +1 -1
  128. package/ios/dist/ViroRenderer/ViroKit.framework/ViroKit +0 -0
  129. package/ios/dist/ViroRenderer/ViroKit.podspec +5 -0
  130. package/ios/dist/include/VRTARSceneNavigator.h +3 -0
  131. package/ios/dist/include/VRTStudioModule.h +6 -0
  132. package/ios/dist/lib/libViroReact.a +0 -0
  133. package/package.json +1 -1
@@ -0,0 +1,174 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.StudioSceneNavigator = StudioSceneNavigator;
37
+ const React = __importStar(require("react"));
38
+ const react_1 = require("react");
39
+ const react_native_1 = require("react-native");
40
+ const ViroARScene_1 = require("../AR/ViroARScene");
41
+ const ViroScene_1 = require("../ViroScene");
42
+ const ViroXRSceneNavigator_1 = require("../ViroXRSceneNavigator");
43
+ const ViroPlatform_1 = require("../Utilities/ViroPlatform");
44
+ const animationRegistry_1 = require("./domain/animationRegistry");
45
+ const studioMaterials_1 = require("./domain/studioMaterials");
46
+ const StudioARScene_1 = require("./StudioARScene");
47
+ const VRTStudioModule_1 = require("./VRTStudioModule");
48
+ function LoadingARScene() { return <ViroARScene_1.ViroARScene />; }
49
+ function LoadingVRScene() { return <ViroScene_1.ViroScene />; }
50
+ const styles = react_native_1.StyleSheet.create({
51
+ loader: {
52
+ position: "absolute",
53
+ top: 0, left: 0, right: 0, bottom: 0,
54
+ justifyContent: "center",
55
+ alignItems: "center",
56
+ backgroundColor: "#000000",
57
+ },
58
+ });
59
+ /**
60
+ * Cross-reality Studio scene navigator. Renders a Studio-authored scene on
61
+ * both AR devices (iOS / non-Quest Android) and Meta Quest (VR).
62
+ *
63
+ * Opening-scene resolution order:
64
+ * 1. `sceneId` prop → use it directly
65
+ * 2. Native project (RVProjectId from manifest) → use `opening_scene.id`
66
+ * 3. Fallback → first scene in the project's scene list
67
+ *
68
+ * On Quest, ViroXRSceneNavigator is not rendered until the scene data is
69
+ * ready. This means VRActivity always launches with the actual content scene
70
+ * as its initial scene, avoiding the LoadingVRScene → replace timing race.
71
+ */
72
+ function StudioSceneNavigator({ sceneId, worldAlignment = "Gravity", autofocus = true, style, onSceneReady, onError, onSceneChange, onExitViro, }) {
73
+ const navigatorRef = (0, react_1.useRef)(null);
74
+ const loadedSceneIdRef = (0, react_1.useRef)(null);
75
+ const onSceneReadyRef = (0, react_1.useRef)(onSceneReady);
76
+ const onErrorRef = (0, react_1.useRef)(onError);
77
+ const onSceneChangeRef = (0, react_1.useRef)(onSceneChange);
78
+ onSceneReadyRef.current = onSceneReady;
79
+ onErrorRef.current = onError;
80
+ onSceneChangeRef.current = onSceneChange;
81
+ // On Quest: holds the resolved scene entry. ViroXRSceneNavigator is not
82
+ // rendered until this is non-null, so VRActivity always launches into content.
83
+ const [vrSceneEntry, setVrSceneEntry] = (0, react_1.useState)(null);
84
+ const resolveSceneId = (0, react_1.useCallback)(async () => {
85
+ if (sceneId)
86
+ return sceneId;
87
+ const projectResult = await VRTStudioModule_1.VRTStudioModule.rvGetProject();
88
+ if (!projectResult.success) {
89
+ throw new Error(projectResult.error ?? "rvGetProject failed");
90
+ }
91
+ if (typeof projectResult.data !== "string") {
92
+ throw new Error("rvGetProject returned no data");
93
+ }
94
+ const { project } = JSON.parse(projectResult.data);
95
+ if (project.opening_scene?.id) {
96
+ return project.opening_scene.id;
97
+ }
98
+ if (project.scenes.length > 0) {
99
+ return project.scenes[0].id;
100
+ }
101
+ throw new Error(`Project ${project.id} has no scenes`);
102
+ }, [sceneId]);
103
+ const loadScene = (0, react_1.useCallback)(async (isCancelled) => {
104
+ await new Promise((resolve) => requestAnimationFrame(() => resolve()));
105
+ if (isCancelled())
106
+ return;
107
+ const resolvedSceneId = await resolveSceneId();
108
+ if (isCancelled())
109
+ return;
110
+ if (loadedSceneIdRef.current === resolvedSceneId)
111
+ return;
112
+ const result = await VRTStudioModule_1.VRTStudioModule.rvGetScene(resolvedSceneId);
113
+ if (isCancelled())
114
+ return;
115
+ if (!result.success) {
116
+ throw new Error(result.error ?? "rvGetScene failed");
117
+ }
118
+ if (typeof result.data !== "string") {
119
+ throw new Error("rvGetScene returned no data");
120
+ }
121
+ const sceneData = JSON.parse(result.data);
122
+ if (isCancelled())
123
+ return;
124
+ loadedSceneIdRef.current = resolvedSceneId;
125
+ // On Quest: pre-register animations and materials before VRActivity launches.
126
+ // This mirrors the module-level registration pattern used by XRSceneContent —
127
+ // native registrations complete before any Viro components mount, eliminating
128
+ // the race between registerAnimations/createMaterials native calls and the
129
+ // Fabric commit that creates those components.
130
+ if (ViroPlatform_1.isQuest) {
131
+ (0, animationRegistry_1.registerSceneAnimations)(sceneData.animations);
132
+ (0, studioMaterials_1.registerStudioMaterialsForAssets)(sceneData.assets);
133
+ }
134
+ const entry = {
135
+ scene: StudioARScene_1.StudioARScene,
136
+ passProps: {
137
+ sceneData,
138
+ onReady: onSceneReadyRef.current,
139
+ onSceneChange: onSceneChangeRef.current,
140
+ },
141
+ };
142
+ if (ViroPlatform_1.isQuest) {
143
+ // On Quest: setting vrSceneEntry triggers ViroXRSceneNavigator to mount
144
+ // with StudioARScene as vrInitialScene — VRActivity gets content immediately.
145
+ setVrSceneEntry(entry);
146
+ }
147
+ else {
148
+ navigatorRef.current?.arSceneNavigator?.push(entry);
149
+ }
150
+ }, [resolveSceneId]);
151
+ (0, react_1.useEffect)(() => {
152
+ let cancelled = false;
153
+ const isCancelled = () => cancelled;
154
+ loadScene(isCancelled).catch((e) => {
155
+ if (cancelled)
156
+ return;
157
+ const err = e instanceof Error ? e : new Error(String(e));
158
+ const handler = onErrorRef.current;
159
+ if (handler)
160
+ handler(err);
161
+ else
162
+ console.error("[Studio] Failed to load scene:", err);
163
+ });
164
+ return () => { cancelled = true; };
165
+ }, [sceneId, loadScene]);
166
+ // On Quest: show a spinner until scene data is ready, then mount
167
+ // ViroXRSceneNavigator (which launches VRActivity with content immediately).
168
+ if (ViroPlatform_1.isQuest && !vrSceneEntry) {
169
+ return (<react_native_1.View style={styles.loader}>
170
+ <react_native_1.ActivityIndicator size="large" color="#ffffff"/>
171
+ </react_native_1.View>);
172
+ }
173
+ return (<ViroXRSceneNavigator_1.ViroXRSceneNavigator ref={navigatorRef} arInitialScene={{ scene: LoadingARScene }} vrInitialScene={vrSceneEntry ?? { scene: LoadingVRScene }} worldAlignment={worldAlignment} autofocus={autofocus} onExitViro={onExitViro} style={style ?? react_native_1.StyleSheet.absoluteFill}/>);
174
+ }
@@ -0,0 +1,15 @@
1
+ export interface StudioModuleResult {
2
+ success: boolean;
3
+ data?: string;
4
+ error?: string;
5
+ }
6
+ export declare const VRTStudioModule: {
7
+ rvGetScene: (sceneId: string) => Promise<StudioModuleResult>;
8
+ /**
9
+ * Fetches the project configured in the app manifest (Android: `com.reactvision.RVProjectId`,
10
+ * iOS: `RVProjectId`). The project ID is baked in by the Expo plugin at build time.
11
+ */
12
+ rvGetProject: () => Promise<StudioModuleResult>;
13
+ /** Returns the configured project ID, or null if not set. */
14
+ rvGetProjectId: () => Promise<string | null>;
15
+ };
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.VRTStudioModule = void 0;
4
+ const react_native_1 = require("react-native");
5
+ const native = react_native_1.NativeModules.VRTStudio;
6
+ const NOT_AVAILABLE = {
7
+ success: false,
8
+ error: "VRTStudio native module not available",
9
+ };
10
+ exports.VRTStudioModule = {
11
+ rvGetScene: (sceneId) => {
12
+ if (!native)
13
+ return Promise.resolve(NOT_AVAILABLE);
14
+ return native.rvGetScene(sceneId);
15
+ },
16
+ /**
17
+ * Fetches the project configured in the app manifest (Android: `com.reactvision.RVProjectId`,
18
+ * iOS: `RVProjectId`). The project ID is baked in by the Expo plugin at build time.
19
+ */
20
+ rvGetProject: () => {
21
+ if (!native)
22
+ return Promise.resolve(NOT_AVAILABLE);
23
+ return native.rvGetProject();
24
+ },
25
+ /** Returns the configured project ID, or null if not set. */
26
+ rvGetProjectId: () => {
27
+ if (!native)
28
+ return Promise.resolve(null);
29
+ return native.rvGetProjectId();
30
+ },
31
+ };
@@ -0,0 +1,11 @@
1
+ import { StudioAnimation } from "../types";
2
+ /**
3
+ * Builds the Viro animation registry object from StudioAnimation rows.
4
+ * The properties field is already in Viro's native keyframe format.
5
+ */
6
+ export declare function buildViroAnimationRegistry(animations: StudioAnimation[]): Record<string, unknown>;
7
+ /**
8
+ * Registers all scene animations with ViroReact.
9
+ * Must be called before any animated Viro components mount.
10
+ */
11
+ export declare function registerSceneAnimations(animations: StudioAnimation[]): void;
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildViroAnimationRegistry = buildViroAnimationRegistry;
4
+ exports.registerSceneAnimations = registerSceneAnimations;
5
+ const ViroAnimations_1 = require("../../Animation/ViroAnimations");
6
+ const MAX_CONCURRENT_ANIMATIONS = 10;
7
+ const MAX_DURATION_MS = 30_000;
8
+ const MAX_POSITION_ABS = 1_000;
9
+ const MAX_SCALE = 100;
10
+ const MIN_SCALE = 0.01;
11
+ /**
12
+ * Builds the Viro animation registry object from StudioAnimation rows.
13
+ * The properties field is already in Viro's native keyframe format.
14
+ */
15
+ function buildViroAnimationRegistry(animations) {
16
+ const registry = {};
17
+ for (const anim of animations) {
18
+ warnAnimationPerformance(anim);
19
+ registry[anim.animation_key] = {
20
+ properties: anim.properties,
21
+ duration: anim.duration_ms ?? 1000,
22
+ delay: anim.delay_ms ?? 0,
23
+ ...(anim.easing ? { easing: anim.easing } : {}),
24
+ };
25
+ }
26
+ return registry;
27
+ }
28
+ /**
29
+ * Registers all scene animations with ViroReact.
30
+ * Must be called before any animated Viro components mount.
31
+ */
32
+ function registerSceneAnimations(animations) {
33
+ if (animations.length === 0)
34
+ return;
35
+ if (animations.length > MAX_CONCURRENT_ANIMATIONS) {
36
+ console.warn(`[Studio/Animation] Scene has ${animations.length} animations. ` +
37
+ `Recommended max is ${MAX_CONCURRENT_ANIMATIONS} for smooth performance.`);
38
+ }
39
+ const registry = buildViroAnimationRegistry(animations);
40
+ ViroAnimations_1.ViroAnimations.registerAnimations(registry);
41
+ console.log(`[Studio/Animation] Registered ${Object.keys(registry).length} animation(s): ${Object.keys(registry).join(", ")}`);
42
+ }
43
+ function warnAnimationPerformance(anim) {
44
+ if ((anim.duration_ms ?? 0) > MAX_DURATION_MS) {
45
+ console.warn(`[Studio/Animation] "${anim.animation_key}" duration ${anim.duration_ms}ms exceeds ` +
46
+ `recommended max of ${MAX_DURATION_MS}ms.`);
47
+ }
48
+ const props = anim.properties;
49
+ if (!props || typeof props !== "object")
50
+ return;
51
+ for (const key of Object.keys(props)) {
52
+ const val = props[key];
53
+ if (typeof val !== "number")
54
+ continue;
55
+ if (key === "scaleX" || key === "scaleY" || key === "scaleZ") {
56
+ if (val > MAX_SCALE || val < MIN_SCALE) {
57
+ console.warn(`[Studio/Animation] "${anim.animation_key}" ${key}=${val} is outside ` +
58
+ `recommended range [${MIN_SCALE}, ${MAX_SCALE}].`);
59
+ }
60
+ }
61
+ if (key === "positionX" || key === "positionY" || key === "positionZ") {
62
+ if (Math.abs(val) > MAX_POSITION_ABS) {
63
+ console.warn(`[Studio/Animation] "${anim.animation_key}" ${key}=${val} is far from origin.`);
64
+ }
65
+ }
66
+ }
67
+ }
@@ -0,0 +1,21 @@
1
+ import { MutableRefObject } from "react";
2
+ import { StudioAnimation, StudioCollisionBinding } from "../types";
3
+ /**
4
+ * Dispatches scene functions for collision bindings matching the canonical pair.
5
+ * Cooldown prevents per-frame spam while physics contacts overlap.
6
+ */
7
+ export declare function dispatchCollisionBindingActions(params: {
8
+ selfPlacementId: string;
9
+ otherTag: string;
10
+ bindingsByPairKey: Map<string, StudioCollisionBinding[]>;
11
+ sceneNavigator?: unknown;
12
+ animations: StudioAnimation[];
13
+ onSceneChange?: (sceneId: string, sceneName: string) => void;
14
+ onAnimationTrigger?: (targetAssetId: string, animationKey: string) => void;
15
+ cooldownMs?: number;
16
+ lastFiredRef: MutableRefObject<Map<string, number>>;
17
+ }): void;
18
+ /**
19
+ * Returns an onCollision handler for a given placement asset ID.
20
+ */
21
+ export declare function createPlacementCollisionHandler(placementId: string, bindingsByPairKey: Map<string, StudioCollisionBinding[]>, sceneNavigator: unknown, animations: StudioAnimation[], lastFiredRef: MutableRefObject<Map<string, number>>, onAnimationTrigger?: (targetAssetId: string, animationKey: string) => void, onSceneChange?: (sceneId: string, sceneName: string) => void): (viroTag: string, collidedPoint: [number, number, number], collidedNormal: [number, number, number]) => void;
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.dispatchCollisionBindingActions = dispatchCollisionBindingActions;
4
+ exports.createPlacementCollisionHandler = createPlacementCollisionHandler;
5
+ const collisionPairKey_1 = require("./collisionPairKey");
6
+ const sceneNavigationHandler_1 = require("./sceneNavigationHandler");
7
+ const DEFAULT_COOLDOWN_MS = 750;
8
+ function pairCooldownKey(pairKey, functionId) {
9
+ return `${pairKey}::${functionId}`;
10
+ }
11
+ /**
12
+ * Dispatches scene functions for collision bindings matching the canonical pair.
13
+ * Cooldown prevents per-frame spam while physics contacts overlap.
14
+ */
15
+ function dispatchCollisionBindingActions(params) {
16
+ const { selfPlacementId, otherTag, bindingsByPairKey, sceneNavigator, animations, onSceneChange, onAnimationTrigger, cooldownMs = DEFAULT_COOLDOWN_MS, lastFiredRef, } = params;
17
+ if (!otherTag)
18
+ return;
19
+ const { asset_x_id, asset_y_id } = (0, collisionPairKey_1.canonicalizeCollisionAssetIds)(selfPlacementId, otherTag);
20
+ const pKey = (0, collisionPairKey_1.collisionPairKey)(asset_x_id, asset_y_id);
21
+ const rows = bindingsByPairKey.get(pKey);
22
+ if (!rows?.length)
23
+ return;
24
+ const now = Date.now();
25
+ const map = lastFiredRef.current;
26
+ for (const row of rows) {
27
+ const fn = row.scene_function;
28
+ if (!fn)
29
+ continue;
30
+ const ck = pairCooldownKey(pKey, row.function_id);
31
+ const last = map.get(ck) ?? 0;
32
+ if (now - last < cooldownMs)
33
+ continue;
34
+ map.set(ck, now);
35
+ (0, sceneNavigationHandler_1.executeFunctionWithRelations)(fn, sceneNavigator, animations, onAnimationTrigger, 0, onSceneChange);
36
+ }
37
+ }
38
+ /**
39
+ * Returns an onCollision handler for a given placement asset ID.
40
+ */
41
+ function createPlacementCollisionHandler(placementId, bindingsByPairKey, sceneNavigator, animations, lastFiredRef, onAnimationTrigger, onSceneChange) {
42
+ return (viroTag) => {
43
+ dispatchCollisionBindingActions({
44
+ selfPlacementId: placementId,
45
+ otherTag: viroTag,
46
+ bindingsByPairKey,
47
+ sceneNavigator,
48
+ animations,
49
+ onSceneChange,
50
+ onAnimationTrigger,
51
+ lastFiredRef,
52
+ });
53
+ };
54
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Canonical collision pair — matches DB constraint where asset_x_id < asset_y_id lexicographically.
3
+ */
4
+ export declare function canonicalizeCollisionAssetIds(a: string, b: string): {
5
+ asset_x_id: string;
6
+ asset_y_id: string;
7
+ };
8
+ export declare function collisionPairKey(assetX: string, assetY: string): string;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.canonicalizeCollisionAssetIds = canonicalizeCollisionAssetIds;
4
+ exports.collisionPairKey = collisionPairKey;
5
+ /**
6
+ * Canonical collision pair — matches DB constraint where asset_x_id < asset_y_id lexicographically.
7
+ */
8
+ function canonicalizeCollisionAssetIds(a, b) {
9
+ return a < b
10
+ ? { asset_x_id: a, asset_y_id: b }
11
+ : { asset_x_id: b, asset_y_id: a };
12
+ }
13
+ function collisionPairKey(assetX, assetY) {
14
+ return `${assetX}|${assetY}`;
15
+ }
@@ -0,0 +1,20 @@
1
+ import { StudioAsset, StudioSceneMeta } from "../types";
2
+ export type DragType = "FixedToWorld" | "FixedToPlane" | undefined;
3
+ export type DragPlane = {
4
+ planePoint: [number, number, number];
5
+ planeNormal: [number, number, number];
6
+ maxDistance: number;
7
+ };
8
+ export declare class DragConfiguration {
9
+ /**
10
+ * Chooses FixedToPlane when the scene uses plane detection, FixedToWorld otherwise.
11
+ * Returns undefined if the asset is not draggable.
12
+ */
13
+ static getDragType(asset: StudioAsset, scene: StudioSceneMeta | null): DragType;
14
+ /**
15
+ * Returns a drag plane that passes through the object's current position,
16
+ * preventing objects from jumping on drag start. maxDistance caps how far
17
+ * objects can travel from the camera.
18
+ */
19
+ static getDragPlane(planeAlignment: string, objectPosition: [number, number, number]): DragPlane;
20
+ }
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DragConfiguration = void 0;
4
+ class DragConfiguration {
5
+ /**
6
+ * Chooses FixedToPlane when the scene uses plane detection, FixedToWorld otherwise.
7
+ * Returns undefined if the asset is not draggable.
8
+ */
9
+ static getDragType(asset, scene) {
10
+ if (!asset.is_draggable)
11
+ return undefined;
12
+ const planeDetection = (scene?.plane_detection ?? "NONE").toUpperCase();
13
+ if (planeDetection === "AUTOMATIC" || planeDetection === "MANUAL") {
14
+ return "FixedToPlane";
15
+ }
16
+ return "FixedToWorld";
17
+ }
18
+ /**
19
+ * Returns a drag plane that passes through the object's current position,
20
+ * preventing objects from jumping on drag start. maxDistance caps how far
21
+ * objects can travel from the camera.
22
+ */
23
+ static getDragPlane(planeAlignment, objectPosition) {
24
+ switch (planeAlignment.toLowerCase()) {
25
+ case "horizontal":
26
+ case "horizontalupward":
27
+ return { planePoint: objectPosition, planeNormal: [0, 1, 0], maxDistance: 1.5 };
28
+ case "horizontaldownward":
29
+ return { planePoint: objectPosition, planeNormal: [0, -1, 0], maxDistance: 1.5 };
30
+ case "vertical":
31
+ return { planePoint: objectPosition, planeNormal: [0, 0, 1], maxDistance: 1.5 };
32
+ default:
33
+ return { planePoint: objectPosition, planeNormal: [0, 1, 0], maxDistance: 1.5 };
34
+ }
35
+ }
36
+ }
37
+ exports.DragConfiguration = DragConfiguration;
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Studio material_config parsing and Viro material definition building.
3
+ * Ported from studio-go/domain/materialConfig.ts — no zod dependency.
4
+ */
5
+ type ShaderModifierStage = {
6
+ uniforms?: string;
7
+ body?: string;
8
+ varyings?: unknown;
9
+ requiresSceneDepth?: boolean;
10
+ requiresCameraTexture?: boolean;
11
+ priority?: unknown;
12
+ };
13
+ export type MaterialConfig = {
14
+ presetName?: string;
15
+ lightingModel: "Constant" | "Lambert" | "Blinn" | "Phong" | "PBR";
16
+ diffuseColor?: string;
17
+ roughness?: number;
18
+ metalness?: number;
19
+ shininess?: number;
20
+ alpha?: number;
21
+ blendMode?: string;
22
+ bloomThreshold?: number | null;
23
+ wrapS?: "Clamp" | "Repeat" | "Mirror";
24
+ wrapT?: "Clamp" | "Repeat" | "Mirror";
25
+ diffuseTexture?: string | null;
26
+ normalTexture?: string | null;
27
+ roughnessTexture?: string | null;
28
+ metalnessTexture?: string | null;
29
+ ambientOcclusionTexture?: string | null;
30
+ specularTexture?: string | null;
31
+ shaderModifiers?: Record<string, ShaderModifierStage | string>;
32
+ materialUniforms?: Array<{
33
+ name: string;
34
+ type: string;
35
+ value: unknown;
36
+ }>;
37
+ transparencyMode?: string;
38
+ cullMode?: string;
39
+ };
40
+ export type ViroMaterialDefinition = Record<string, unknown>;
41
+ export declare function materialConfigNeedsTimeUniform(config: MaterialConfig): boolean;
42
+ /**
43
+ * True if the shader uses _rf_vpw/_rf_vph viewport uniforms.
44
+ * These must be pushed via ViroMaterials.updateShaderUniform on mount and orientation change.
45
+ */
46
+ export declare function materialConfigNeedsViewportUniforms(config: MaterialConfig): boolean;
47
+ export declare function studioMaterialName(assetId: string): string;
48
+ /**
49
+ * Parses `scene_assets.material_config` JSON. Returns null if missing or invalid.
50
+ */
51
+ export declare function parseMaterialConfig(raw: unknown): MaterialConfig | null;
52
+ /**
53
+ * Maps a validated Studio material_config to Viro's `createMaterials` definition shape.
54
+ */
55
+ export declare function buildViroMaterialDefinition(config: MaterialConfig): ViroMaterialDefinition;
56
+ export {};