@reactvision/react-viro 2.57.3 → 2.57.4
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/android/react_viro/react_viro-release.aar +0 -0
- package/components/Studio/StudioARScene.tsx +153 -4
- package/components/Studio/StudioSceneErrorBoundary.tsx +48 -0
- package/components/Studio/StudioSceneNavigator.tsx +186 -41
- package/components/Studio/VRTStudioModule.ts +26 -0
- package/components/Studio/domain/dragConfiguration.ts +6 -4
- package/components/Studio/domain/materialConfig.ts +75 -42
- package/components/Studio/domain/physicsConfig.ts +75 -32
- package/components/Studio/domain/viroNodeFactory.tsx +53 -14
- package/components/Studio/index.ts +4 -0
- package/components/Studio/types.ts +3 -8
- package/components/Utilities/ViroVersion.ts +1 -1
- package/components/ViroXRSceneNavigator.tsx +2 -0
- package/dist/components/Studio/StudioARScene.d.ts +6 -0
- package/dist/components/Studio/StudioARScene.js +101 -6
- package/dist/components/Studio/StudioSceneErrorBoundary.d.ts +28 -0
- package/dist/components/Studio/StudioSceneErrorBoundary.js +31 -0
- package/dist/components/Studio/StudioSceneNavigator.d.ts +28 -3
- package/dist/components/Studio/StudioSceneNavigator.js +84 -20
- package/dist/components/Studio/VRTStudioModule.d.ts +17 -0
- package/dist/components/Studio/VRTStudioModule.js +14 -0
- package/dist/components/Studio/domain/dragConfiguration.d.ts +5 -3
- package/dist/components/Studio/domain/dragConfiguration.js +5 -3
- package/dist/components/Studio/domain/materialConfig.d.ts +0 -1
- package/dist/components/Studio/domain/materialConfig.js +12 -5
- package/dist/components/Studio/domain/physicsConfig.d.ts +0 -1
- package/dist/components/Studio/domain/physicsConfig.js +18 -6
- package/dist/components/Studio/domain/viroNodeFactory.d.ts +2 -2
- package/dist/components/Studio/domain/viroNodeFactory.js +31 -18
- package/dist/components/Studio/index.d.ts +1 -0
- package/dist/components/Studio/types.d.ts +1 -0
- package/dist/components/Utilities/ViroVersion.d.ts +1 -1
- package/dist/components/Utilities/ViroVersion.js +1 -1
- package/dist/components/ViroXRSceneNavigator.d.ts +2 -0
- package/dist/index.d.ts +1 -1
- package/index.ts +2 -0
- package/ios/dist/include/RVStudioWatermarkState.h +25 -0
- package/ios/dist/lib/libViroReact.a +0 -0
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { StudioAsset, StudioSceneMeta } from "../types";
|
|
2
2
|
|
|
3
|
-
export type DragType = "
|
|
3
|
+
export type DragType = "FixedToPlane" | "FixedDistance" | undefined;
|
|
4
4
|
|
|
5
5
|
export type DragPlane = {
|
|
6
6
|
planePoint: [number, number, number];
|
|
@@ -10,8 +10,10 @@ export type DragPlane = {
|
|
|
10
10
|
|
|
11
11
|
export class DragConfiguration {
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
13
|
+
* FixedToPlane when the scene uses plane detection; otherwise FixedDistance,
|
|
14
|
+
* which keeps the object at its grab distance and follows the finger.
|
|
15
|
+
* (FixedToWorld raycast-snapped objects toward the camera on drag start, so
|
|
16
|
+
* they appeared to grow and were hard to place.) undefined if not draggable.
|
|
15
17
|
*/
|
|
16
18
|
static getDragType(asset: StudioAsset, scene: StudioSceneMeta | null): DragType {
|
|
17
19
|
if (!asset.is_draggable) return undefined;
|
|
@@ -21,7 +23,7 @@ export class DragConfiguration {
|
|
|
21
23
|
if (planeDetection === "AUTOMATIC" || planeDetection === "MANUAL") {
|
|
22
24
|
return "FixedToPlane";
|
|
23
25
|
}
|
|
24
|
-
return "
|
|
26
|
+
return "FixedDistance";
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
/**
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Studio material_config parsing and Viro material definition building.
|
|
3
|
-
* Ported from studio-go/domain/materialConfig.ts — no zod dependency.
|
|
4
3
|
*/
|
|
5
4
|
|
|
6
5
|
// ─── Types ────────────────────────────────────────────────────────────────────
|
|
@@ -51,7 +50,9 @@ const TEXTURE_KEYS = [
|
|
|
51
50
|
"specularTexture",
|
|
52
51
|
] as const;
|
|
53
52
|
|
|
54
|
-
function textureToViro(
|
|
53
|
+
function textureToViro(
|
|
54
|
+
uri: string | null | undefined
|
|
55
|
+
): { uri: string } | undefined {
|
|
55
56
|
if (uri == null || uri === "") return undefined;
|
|
56
57
|
return { uri };
|
|
57
58
|
}
|
|
@@ -76,7 +77,9 @@ const TIME_WORD_RE = /\btime\b/i;
|
|
|
76
77
|
const CAMERA_TEXTURE_RE = /\bcamera_texture\b/;
|
|
77
78
|
const RF_VIEWPORT_RE = /\b_rf_vpw\b|\b_rf_vph\b/;
|
|
78
79
|
|
|
79
|
-
export function materialConfigNeedsTimeUniform(
|
|
80
|
+
export function materialConfigNeedsTimeUniform(
|
|
81
|
+
config: MaterialConfig
|
|
82
|
+
): boolean {
|
|
80
83
|
if (config.materialUniforms?.some((u) => u.name === "time")) return true;
|
|
81
84
|
return collectShaderModifierStrings(config).some((s) => TIME_WORD_RE.test(s));
|
|
82
85
|
}
|
|
@@ -85,8 +88,12 @@ export function materialConfigNeedsTimeUniform(config: MaterialConfig): boolean
|
|
|
85
88
|
* True if the shader uses _rf_vpw/_rf_vph viewport uniforms.
|
|
86
89
|
* These must be pushed via ViroMaterials.updateShaderUniform on mount and orientation change.
|
|
87
90
|
*/
|
|
88
|
-
export function materialConfigNeedsViewportUniforms(
|
|
89
|
-
|
|
91
|
+
export function materialConfigNeedsViewportUniforms(
|
|
92
|
+
config: MaterialConfig
|
|
93
|
+
): boolean {
|
|
94
|
+
return collectShaderModifierStrings(config).some((s) =>
|
|
95
|
+
RF_VIEWPORT_RE.test(s)
|
|
96
|
+
);
|
|
90
97
|
}
|
|
91
98
|
|
|
92
99
|
/**
|
|
@@ -101,7 +108,8 @@ function injectMissingGlslDeclarations(uniforms: string, body: string): string {
|
|
|
101
108
|
result;
|
|
102
109
|
}
|
|
103
110
|
if (RF_VIEWPORT_RE.test(body) && !/\b_rf_vpw\b/.test(result)) {
|
|
104
|
-
result =
|
|
111
|
+
result =
|
|
112
|
+
"uniform highp float _rf_vpw;\nuniform highp float _rf_vph;\n" + result;
|
|
105
113
|
}
|
|
106
114
|
return result;
|
|
107
115
|
}
|
|
@@ -112,13 +120,20 @@ function injectMissingGlslDeclarations(uniforms: string, body: string): string {
|
|
|
112
120
|
* GLSL only, we add the runtime binding here.
|
|
113
121
|
*/
|
|
114
122
|
function mergeMaterialUniformsForViro(
|
|
115
|
-
config: MaterialConfig
|
|
123
|
+
config: MaterialConfig
|
|
116
124
|
): Array<{ name: string; type: string; value: unknown }> | undefined {
|
|
117
125
|
const list = config.materialUniforms
|
|
118
|
-
? config.materialUniforms.map((u) => ({
|
|
126
|
+
? config.materialUniforms.map((u) => ({
|
|
127
|
+
name: u.name,
|
|
128
|
+
type: u.type,
|
|
129
|
+
value: u.value,
|
|
130
|
+
}))
|
|
119
131
|
: [];
|
|
120
132
|
|
|
121
|
-
if (
|
|
133
|
+
if (
|
|
134
|
+
materialConfigNeedsTimeUniform(config) &&
|
|
135
|
+
!list.some((u) => u.name === "time")
|
|
136
|
+
) {
|
|
122
137
|
list.push({ name: "time", type: "float", value: 0 });
|
|
123
138
|
}
|
|
124
139
|
|
|
@@ -139,7 +154,7 @@ function mergeMaterialUniformsForViro(
|
|
|
139
154
|
* layer binds the camera feed even when the DB JSON omits `requiresCameraTexture`.
|
|
140
155
|
*/
|
|
141
156
|
function normalizeShaderModifiersForViro(
|
|
142
|
-
mods: NonNullable<MaterialConfig["shaderModifiers"]
|
|
157
|
+
mods: NonNullable<MaterialConfig["shaderModifiers"]>
|
|
143
158
|
): Record<string, string | object> {
|
|
144
159
|
const out: Record<string, string | object> = {};
|
|
145
160
|
for (const [key, stage] of Object.entries(mods)) {
|
|
@@ -199,39 +214,51 @@ export function parseMaterialConfig(raw: unknown): MaterialConfig | null {
|
|
|
199
214
|
|
|
200
215
|
try {
|
|
201
216
|
const config: MaterialConfig = {
|
|
202
|
-
lightingModel:
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
217
|
+
lightingModel: (["Constant", "Lambert", "Blinn", "Phong", "PBR"].includes(
|
|
218
|
+
r.lightingModel as string
|
|
219
|
+
)
|
|
220
|
+
? r.lightingModel
|
|
221
|
+
: "PBR") as MaterialConfig["lightingModel"],
|
|
206
222
|
};
|
|
207
223
|
|
|
208
|
-
if (typeof r.presetName === "string")
|
|
209
|
-
if (typeof r.diffuseColor === "string")
|
|
210
|
-
|
|
211
|
-
if (typeof r.
|
|
212
|
-
if (typeof r.
|
|
213
|
-
if (typeof r.
|
|
214
|
-
if (typeof r.
|
|
224
|
+
if (typeof r.presetName === "string") config.presetName = r.presetName;
|
|
225
|
+
if (typeof r.diffuseColor === "string")
|
|
226
|
+
config.diffuseColor = r.diffuseColor;
|
|
227
|
+
if (typeof r.roughness === "number") config.roughness = r.roughness;
|
|
228
|
+
if (typeof r.metalness === "number") config.metalness = r.metalness;
|
|
229
|
+
if (typeof r.shininess === "number") config.shininess = r.shininess;
|
|
230
|
+
if (typeof r.alpha === "number") config.alpha = r.alpha;
|
|
231
|
+
if (typeof r.blendMode === "string") config.blendMode = r.blendMode;
|
|
215
232
|
if (r.bloomThreshold != null && typeof r.bloomThreshold === "number")
|
|
216
|
-
|
|
217
|
-
if (["Clamp","Repeat","Mirror"].includes(r.wrapS as string))
|
|
218
|
-
|
|
219
|
-
if (
|
|
220
|
-
|
|
233
|
+
config.bloomThreshold = r.bloomThreshold;
|
|
234
|
+
if (["Clamp", "Repeat", "Mirror"].includes(r.wrapS as string))
|
|
235
|
+
config.wrapS = r.wrapS as any;
|
|
236
|
+
if (["Clamp", "Repeat", "Mirror"].includes(r.wrapT as string))
|
|
237
|
+
config.wrapT = r.wrapT as any;
|
|
238
|
+
if (typeof r.transparencyMode === "string")
|
|
239
|
+
config.transparencyMode = r.transparencyMode;
|
|
240
|
+
if (typeof r.cullMode === "string") config.cullMode = r.cullMode;
|
|
221
241
|
|
|
222
242
|
for (const key of TEXTURE_KEYS) {
|
|
223
243
|
const v = r[key];
|
|
224
244
|
if (typeof v === "string" || v === null) (config as any)[key] = v;
|
|
225
245
|
}
|
|
226
246
|
|
|
227
|
-
if (
|
|
228
|
-
|
|
247
|
+
if (
|
|
248
|
+
r.shaderModifiers &&
|
|
249
|
+
typeof r.shaderModifiers === "object" &&
|
|
250
|
+
!Array.isArray(r.shaderModifiers)
|
|
251
|
+
) {
|
|
252
|
+
config.shaderModifiers = r.shaderModifiers as Record<
|
|
253
|
+
string,
|
|
254
|
+
ShaderModifierStage | string
|
|
255
|
+
>;
|
|
229
256
|
}
|
|
230
257
|
|
|
231
258
|
if (Array.isArray(r.materialUniforms)) {
|
|
232
259
|
config.materialUniforms = r.materialUniforms.filter(
|
|
233
260
|
(u): u is { name: string; type: string; value: unknown } =>
|
|
234
|
-
u && typeof u.name === "string" && typeof u.type === "string"
|
|
261
|
+
u && typeof u.name === "string" && typeof u.type === "string"
|
|
235
262
|
);
|
|
236
263
|
}
|
|
237
264
|
|
|
@@ -245,20 +272,24 @@ export function parseMaterialConfig(raw: unknown): MaterialConfig | null {
|
|
|
245
272
|
/**
|
|
246
273
|
* Maps a validated Studio material_config to Viro's `createMaterials` definition shape.
|
|
247
274
|
*/
|
|
248
|
-
export function buildViroMaterialDefinition(
|
|
275
|
+
export function buildViroMaterialDefinition(
|
|
276
|
+
config: MaterialConfig
|
|
277
|
+
): ViroMaterialDefinition {
|
|
249
278
|
const out: ViroMaterialDefinition = { lightingModel: config.lightingModel };
|
|
250
279
|
|
|
251
|
-
if (config.diffuseColor
|
|
252
|
-
if (config.roughness
|
|
253
|
-
if (config.metalness
|
|
254
|
-
if (config.shininess
|
|
255
|
-
if (config.alpha
|
|
256
|
-
if (config.blendMode
|
|
257
|
-
if (config.bloomThreshold!= undefined)
|
|
258
|
-
|
|
259
|
-
if (config.
|
|
260
|
-
if (config.
|
|
261
|
-
if (config.
|
|
280
|
+
if (config.diffuseColor !== undefined) out.diffuseColor = config.diffuseColor;
|
|
281
|
+
if (config.roughness !== undefined) out.roughness = config.roughness;
|
|
282
|
+
if (config.metalness !== undefined) out.metalness = config.metalness;
|
|
283
|
+
if (config.shininess !== undefined) out.shininess = config.shininess;
|
|
284
|
+
if (config.alpha !== undefined) out.alpha = config.alpha;
|
|
285
|
+
if (config.blendMode !== undefined) out.blendMode = config.blendMode;
|
|
286
|
+
if (config.bloomThreshold != undefined)
|
|
287
|
+
out.bloomThreshold = config.bloomThreshold;
|
|
288
|
+
if (config.wrapS !== undefined) out.wrapS = config.wrapS;
|
|
289
|
+
if (config.wrapT !== undefined) out.wrapT = config.wrapT;
|
|
290
|
+
if (config.transparencyMode !== undefined)
|
|
291
|
+
out.transparencyMode = config.transparencyMode;
|
|
292
|
+
if (config.cullMode !== undefined) out.cullMode = config.cullMode;
|
|
262
293
|
|
|
263
294
|
for (const key of TEXTURE_KEYS) {
|
|
264
295
|
const mapped = textureToViro((config as any)[key]);
|
|
@@ -266,7 +297,9 @@ export function buildViroMaterialDefinition(config: MaterialConfig): ViroMateria
|
|
|
266
297
|
}
|
|
267
298
|
|
|
268
299
|
if (config.shaderModifiers !== undefined) {
|
|
269
|
-
out.shaderModifiers = normalizeShaderModifiersForViro(
|
|
300
|
+
out.shaderModifiers = normalizeShaderModifiersForViro(
|
|
301
|
+
config.shaderModifiers
|
|
302
|
+
);
|
|
270
303
|
}
|
|
271
304
|
|
|
272
305
|
const mergedUniforms = mergeMaterialUniformsForViro(config);
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Studio physics_config and physics_world_config parsing and Viro prop building.
|
|
3
|
-
* Ported from studio-go/domain/physicsConfig.ts — no zod dependency.
|
|
4
3
|
*/
|
|
5
4
|
|
|
6
5
|
// ─── Types ────────────────────────────────────────────────────────────────────
|
|
@@ -12,7 +11,15 @@ type ForceEntry = { value: Vec3; position?: Vec3 };
|
|
|
12
11
|
type PhysicsShape =
|
|
13
12
|
| { type: "Box"; params: [number, number, number] }
|
|
14
13
|
| { type: "Sphere"; params: [number] }
|
|
15
|
-
| {
|
|
14
|
+
| {
|
|
15
|
+
type: "Compound";
|
|
16
|
+
children: Array<{
|
|
17
|
+
type: "Box" | "Sphere";
|
|
18
|
+
params: number[];
|
|
19
|
+
position: Vec3;
|
|
20
|
+
rotation?: Vec3;
|
|
21
|
+
}>;
|
|
22
|
+
};
|
|
16
23
|
|
|
17
24
|
export type PhysicsBodyConfig = {
|
|
18
25
|
enabled: boolean;
|
|
@@ -42,7 +49,9 @@ export type BuildViroPhysicsBodyOptions = {
|
|
|
42
49
|
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
43
50
|
|
|
44
51
|
function isVec3(v: unknown): v is Vec3 {
|
|
45
|
-
return
|
|
52
|
+
return (
|
|
53
|
+
Array.isArray(v) && v.length === 3 && v.every((n) => typeof n === "number")
|
|
54
|
+
);
|
|
46
55
|
}
|
|
47
56
|
|
|
48
57
|
function parseShape(raw: unknown): PhysicsShape | undefined {
|
|
@@ -56,11 +65,24 @@ function parseShape(raw: unknown): PhysicsShape | undefined {
|
|
|
56
65
|
return { type: "Sphere", params: [r.params[0] as number] };
|
|
57
66
|
}
|
|
58
67
|
if (r.type === "Compound" && Array.isArray(r.children)) {
|
|
59
|
-
const children = (r.children as unknown[]).filter(
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
68
|
+
const children = (r.children as unknown[]).filter(
|
|
69
|
+
(
|
|
70
|
+
c
|
|
71
|
+
): c is {
|
|
72
|
+
type: "Box" | "Sphere";
|
|
73
|
+
params: number[];
|
|
74
|
+
position: Vec3;
|
|
75
|
+
rotation?: Vec3;
|
|
76
|
+
} => {
|
|
77
|
+
if (!c || typeof c !== "object") return false;
|
|
78
|
+
const ch = c as Record<string, unknown>;
|
|
79
|
+
return (
|
|
80
|
+
(ch.type === "Box" || ch.type === "Sphere") &&
|
|
81
|
+
Array.isArray(ch.params) &&
|
|
82
|
+
isVec3(ch.position)
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
);
|
|
64
86
|
if (children.length > 0) return { type: "Compound", children };
|
|
65
87
|
}
|
|
66
88
|
return undefined;
|
|
@@ -68,12 +90,17 @@ function parseShape(raw: unknown): PhysicsShape | undefined {
|
|
|
68
90
|
|
|
69
91
|
function mapShapeToViro(shape: PhysicsShape): Record<string, unknown> {
|
|
70
92
|
if (shape.type === "Box") return { type: "Box", params: [...shape.params] };
|
|
71
|
-
if (shape.type === "Sphere")
|
|
93
|
+
if (shape.type === "Sphere")
|
|
94
|
+
return { type: "Sphere", params: [...shape.params] };
|
|
72
95
|
return {
|
|
73
96
|
type: "Compound",
|
|
74
97
|
params: [],
|
|
75
98
|
children: shape.children.map((c) => {
|
|
76
|
-
const base: Record<string, unknown> = {
|
|
99
|
+
const base: Record<string, unknown> = {
|
|
100
|
+
type: c.type,
|
|
101
|
+
params: [...c.params],
|
|
102
|
+
position: [...c.position],
|
|
103
|
+
};
|
|
77
104
|
if (c.rotation) base.rotation = [...c.rotation];
|
|
78
105
|
return base;
|
|
79
106
|
}),
|
|
@@ -84,13 +111,15 @@ function normalizeTorque(torque: Vec3 | Vec3[]): Vec3 {
|
|
|
84
111
|
if (Array.isArray(torque[0])) {
|
|
85
112
|
return (torque as Vec3[]).reduce<Vec3>(
|
|
86
113
|
(acc, t) => [acc[0] + t[0], acc[1] + t[1], acc[2] + t[2]],
|
|
87
|
-
[0, 0, 0]
|
|
114
|
+
[0, 0, 0]
|
|
88
115
|
);
|
|
89
116
|
}
|
|
90
117
|
return [...(torque as Vec3)] as Vec3;
|
|
91
118
|
}
|
|
92
119
|
|
|
93
|
-
function normalizeForce(
|
|
120
|
+
function normalizeForce(
|
|
121
|
+
force: ForceEntry | ForceEntry[]
|
|
122
|
+
): Array<{ value: number[]; position?: number[] }> {
|
|
94
123
|
const arr = Array.isArray(force) ? force : [force];
|
|
95
124
|
return arr.map((f) => ({
|
|
96
125
|
value: [...f.value],
|
|
@@ -103,7 +132,9 @@ function normalizeForce(force: ForceEntry | ForceEntry[]): Array<{ value: number
|
|
|
103
132
|
/**
|
|
104
133
|
* Parses `scene.physics_world_config` JSON. Returns null if missing or invalid.
|
|
105
134
|
*/
|
|
106
|
-
export function parsePhysicsWorldConfig(
|
|
135
|
+
export function parsePhysicsWorldConfig(
|
|
136
|
+
raw: unknown
|
|
137
|
+
): PhysicsWorldConfig | null {
|
|
107
138
|
if (raw == null || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
108
139
|
const r = raw as Record<string, unknown>;
|
|
109
140
|
try {
|
|
@@ -124,9 +155,11 @@ export function parsePhysicsBodyConfig(raw: unknown): PhysicsBodyConfig | null {
|
|
|
124
155
|
if (raw == null || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
125
156
|
const r = raw as Record<string, unknown>;
|
|
126
157
|
try {
|
|
127
|
-
const type = (
|
|
128
|
-
|
|
129
|
-
|
|
158
|
+
const type = (
|
|
159
|
+
["Dynamic", "Kinematic", "Static"].includes(r.type as string)
|
|
160
|
+
? r.type
|
|
161
|
+
: undefined
|
|
162
|
+
) as PhysicsBodyConfig["type"] | undefined;
|
|
130
163
|
if (!type) return null;
|
|
131
164
|
|
|
132
165
|
const mass = typeof r.mass === "number" ? r.mass : 0;
|
|
@@ -139,12 +172,12 @@ export function parsePhysicsBodyConfig(raw: unknown): PhysicsBodyConfig | null {
|
|
|
139
172
|
const shape = parseShape(r.shape);
|
|
140
173
|
if (shape) config.shape = shape;
|
|
141
174
|
if (typeof r.restitution === "number") config.restitution = r.restitution;
|
|
142
|
-
if (typeof r.friction === "number")
|
|
143
|
-
if (typeof r.useGravity === "boolean") config.useGravity
|
|
144
|
-
if (typeof r.viroTag === "string")
|
|
145
|
-
if (isVec3(r.velocity))
|
|
146
|
-
if (r.torque != null)
|
|
147
|
-
if (r.force != null)
|
|
175
|
+
if (typeof r.friction === "number") config.friction = r.friction;
|
|
176
|
+
if (typeof r.useGravity === "boolean") config.useGravity = r.useGravity;
|
|
177
|
+
if (typeof r.viroTag === "string") config.viroTag = r.viroTag;
|
|
178
|
+
if (isVec3(r.velocity)) config.velocity = r.velocity;
|
|
179
|
+
if (r.torque != null) config.torque = r.torque as Vec3 | Vec3[];
|
|
180
|
+
if (r.force != null) config.force = r.force as ForceEntry | ForceEntry[];
|
|
148
181
|
|
|
149
182
|
return config;
|
|
150
183
|
} catch {
|
|
@@ -166,23 +199,33 @@ export function buildViroPhysicsWorld(config: PhysicsWorldConfig): {
|
|
|
166
199
|
/** Maps validated Studio physics_config to Viro `physicsBody` prop. */
|
|
167
200
|
export function buildViroPhysicsBody(
|
|
168
201
|
config: PhysicsBodyConfig,
|
|
169
|
-
options?: BuildViroPhysicsBodyOptions
|
|
202
|
+
options?: BuildViroPhysicsBodyOptions
|
|
170
203
|
): Record<string, unknown> {
|
|
171
204
|
const kinematicDrag =
|
|
172
|
-
options?.kinematicDragOverride === true &&
|
|
205
|
+
options?.kinematicDragOverride === true &&
|
|
206
|
+
config.type === "Dynamic" &&
|
|
207
|
+
config.enabled;
|
|
173
208
|
|
|
174
209
|
const type = kinematicDrag ? "Kinematic" : config.type;
|
|
175
210
|
const mass = kinematicDrag ? 0 : config.mass;
|
|
176
|
-
const shape = mapShapeToViro(
|
|
211
|
+
const shape = mapShapeToViro(
|
|
212
|
+
config.shape ?? { type: "Box", params: [1, 1, 1] }
|
|
213
|
+
);
|
|
177
214
|
|
|
178
|
-
const body: Record<string, unknown> = {
|
|
215
|
+
const body: Record<string, unknown> = {
|
|
216
|
+
type,
|
|
217
|
+
mass,
|
|
218
|
+
shape,
|
|
219
|
+
enabled: config.enabled,
|
|
220
|
+
};
|
|
179
221
|
|
|
180
222
|
if (config.restitution !== undefined) body.restitution = config.restitution;
|
|
181
|
-
if (config.friction
|
|
182
|
-
if (config.useGravity
|
|
183
|
-
|
|
184
|
-
if (config.
|
|
185
|
-
if (config.
|
|
223
|
+
if (config.friction !== undefined) body.friction = config.friction;
|
|
224
|
+
if (config.useGravity !== undefined)
|
|
225
|
+
body.useGravity = kinematicDrag ? false : config.useGravity;
|
|
226
|
+
if (config.velocity !== undefined) body.velocity = [...config.velocity];
|
|
227
|
+
if (config.torque !== undefined) body.torque = normalizeTorque(config.torque);
|
|
228
|
+
if (config.force !== undefined) body.force = normalizeForce(config.force);
|
|
186
229
|
|
|
187
230
|
return body;
|
|
188
231
|
}
|
|
@@ -193,7 +236,7 @@ export function buildViroPhysicsBody(
|
|
|
193
236
|
*/
|
|
194
237
|
export function shouldUseKinematicPhysicsDrag(
|
|
195
238
|
asset: { is_draggable: boolean },
|
|
196
|
-
config: PhysicsBodyConfig | null
|
|
239
|
+
config: PhysicsBodyConfig | null
|
|
197
240
|
): boolean {
|
|
198
241
|
return (
|
|
199
242
|
asset.is_draggable === true &&
|
|
@@ -20,7 +20,11 @@ import {
|
|
|
20
20
|
} from "./apiRequestHelpers";
|
|
21
21
|
import { parseMaterialConfig, studioMaterialName } from "./materialConfig";
|
|
22
22
|
import { DragConfiguration } from "./dragConfiguration";
|
|
23
|
-
import {
|
|
23
|
+
import {
|
|
24
|
+
buildViroPhysicsBody,
|
|
25
|
+
parsePhysicsBodyConfig,
|
|
26
|
+
shouldUseKinematicPhysicsDrag,
|
|
27
|
+
} from "./physicsConfig";
|
|
24
28
|
import { StudioVariableStore } from "./variableStore";
|
|
25
29
|
import { StudioVisibilityStore } from "./visibilityStore";
|
|
26
30
|
|
|
@@ -54,6 +58,7 @@ export function createNodeConfig(
|
|
|
54
58
|
scene: StudioSceneMeta | null,
|
|
55
59
|
onAnimationTrigger?: (targetAssetId: string, animKey: string) => void,
|
|
56
60
|
animationStates?: Record<string, ViroAnimationProp>,
|
|
61
|
+
isDragActive?: (assetId: string) => boolean,
|
|
57
62
|
onSceneChange?: (sceneId: string, sceneName: string) => void,
|
|
58
63
|
runtimeCtx?: SequenceRuntimeContext
|
|
59
64
|
): NodeConfig {
|
|
@@ -107,8 +112,12 @@ export function createNodeConfig(
|
|
|
107
112
|
}
|
|
108
113
|
|
|
109
114
|
const parsedPhysics = parsePhysicsBodyConfig(asset.physics_config);
|
|
115
|
+
const dragActive = isDragActive?.(asset.id) ?? false;
|
|
110
116
|
const physicsBody = parsedPhysics
|
|
111
|
-
? buildViroPhysicsBody(parsedPhysics
|
|
117
|
+
? buildViroPhysicsBody(parsedPhysics, {
|
|
118
|
+
kinematicDragOverride:
|
|
119
|
+
dragActive && shouldUseKinematicPhysicsDrag(asset, parsedPhysics),
|
|
120
|
+
})
|
|
112
121
|
: undefined;
|
|
113
122
|
const viroTag = parsedPhysics ? asset.id : undefined;
|
|
114
123
|
|
|
@@ -194,6 +203,7 @@ function create3DObject(
|
|
|
194
203
|
asset: StudioAsset,
|
|
195
204
|
config: NodeConfig,
|
|
196
205
|
onAssetLoaded?: (id: string) => void,
|
|
206
|
+
notifyPhysicsDrag?: (assetId: string) => void,
|
|
197
207
|
onCollision?: (
|
|
198
208
|
viroTag: string,
|
|
199
209
|
collidedPoint: [number, number, number],
|
|
@@ -241,7 +251,9 @@ function create3DObject(
|
|
|
241
251
|
}
|
|
242
252
|
// Viro derives native canDrag from `onDrag != undefined`; without this prop
|
|
243
253
|
// the drag recognizer is never attached, even when dragType is set.
|
|
244
|
-
{...(config.dragType
|
|
254
|
+
{...(config.dragType
|
|
255
|
+
? { onDrag: () => notifyPhysicsDrag?.(asset.id) }
|
|
256
|
+
: {})}
|
|
245
257
|
{...(shaderOverrides ? { shaderOverrides } : {})}
|
|
246
258
|
{...(config.physicsBody
|
|
247
259
|
? { physicsBody: config.physicsBody as any, viroTag: config.viroTag }
|
|
@@ -254,7 +266,8 @@ function create3DObject(
|
|
|
254
266
|
function createImage(
|
|
255
267
|
asset: StudioAsset,
|
|
256
268
|
config: NodeConfig,
|
|
257
|
-
onAssetLoaded?: (id: string) => void
|
|
269
|
+
onAssetLoaded?: (id: string) => void,
|
|
270
|
+
notifyPhysicsDrag?: (assetId: string) => void
|
|
258
271
|
): React.ReactElement | null {
|
|
259
272
|
if (!asset.file_url) {
|
|
260
273
|
console.warn(`[Studio] Image "${asset.name}" has no file_url`);
|
|
@@ -273,7 +286,9 @@ function createImage(
|
|
|
273
286
|
onClick={config.onClick}
|
|
274
287
|
onLoadEnd={() => onAssetLoaded?.(asset.id)}
|
|
275
288
|
onError={(e) => console.error(`[Studio] Image "${asset.name}" error:`, e)}
|
|
276
|
-
{...(config.dragType
|
|
289
|
+
{...(config.dragType
|
|
290
|
+
? { onDrag: () => notifyPhysicsDrag?.(asset.id) }
|
|
291
|
+
: {})}
|
|
277
292
|
/>
|
|
278
293
|
);
|
|
279
294
|
}
|
|
@@ -288,10 +303,11 @@ const VariableText: React.FC<{
|
|
|
288
303
|
asset: StudioAsset;
|
|
289
304
|
config: NodeConfig;
|
|
290
305
|
store?: StudioVariableStore;
|
|
306
|
+
notifyPhysicsDrag?: (assetId: string) => void;
|
|
291
307
|
// Injected by VisibleNode via cloneElement; TEXT is the only node type that
|
|
292
308
|
// is a component wrapper, so it forwards visibility to its ViroText.
|
|
293
309
|
visible?: boolean;
|
|
294
|
-
}> = ({ asset, config, store, visible }) => {
|
|
310
|
+
}> = ({ asset, config, store, notifyPhysicsDrag, visible }) => {
|
|
295
311
|
const template = asset.name ?? "";
|
|
296
312
|
const compute = () =>
|
|
297
313
|
store
|
|
@@ -323,7 +339,9 @@ const VariableText: React.FC<{
|
|
|
323
339
|
color: "#FFFFFF",
|
|
324
340
|
textAlign: "center",
|
|
325
341
|
}}
|
|
326
|
-
{...(config.dragType
|
|
342
|
+
{...(config.dragType
|
|
343
|
+
? { onDrag: () => notifyPhysicsDrag?.(asset.id) }
|
|
344
|
+
: {})}
|
|
327
345
|
/>
|
|
328
346
|
);
|
|
329
347
|
};
|
|
@@ -357,16 +375,24 @@ const VisibleNode: React.FC<{
|
|
|
357
375
|
function createText(
|
|
358
376
|
asset: StudioAsset,
|
|
359
377
|
config: NodeConfig,
|
|
378
|
+
notifyPhysicsDrag?: (assetId: string) => void,
|
|
360
379
|
store?: StudioVariableStore
|
|
361
380
|
): React.ReactElement {
|
|
362
381
|
return (
|
|
363
|
-
<VariableText
|
|
382
|
+
<VariableText
|
|
383
|
+
key={asset.id}
|
|
384
|
+
asset={asset}
|
|
385
|
+
config={config}
|
|
386
|
+
store={store}
|
|
387
|
+
notifyPhysicsDrag={notifyPhysicsDrag}
|
|
388
|
+
/>
|
|
364
389
|
);
|
|
365
390
|
}
|
|
366
391
|
|
|
367
392
|
function createVideo(
|
|
368
393
|
asset: StudioAsset,
|
|
369
|
-
config: NodeConfig
|
|
394
|
+
config: NodeConfig,
|
|
395
|
+
notifyPhysicsDrag?: (assetId: string) => void
|
|
370
396
|
): React.ReactElement | null {
|
|
371
397
|
if (!asset.file_url) {
|
|
372
398
|
console.warn(`[Studio] Video "${asset.name}" has no file_url`);
|
|
@@ -386,7 +412,9 @@ function createVideo(
|
|
|
386
412
|
loop={true}
|
|
387
413
|
muted={false}
|
|
388
414
|
onError={(e) => console.error(`[Studio] Video "${asset.name}" error:`, e)}
|
|
389
|
-
{...(config.dragType
|
|
415
|
+
{...(config.dragType
|
|
416
|
+
? { onDrag: () => notifyPhysicsDrag?.(asset.id) }
|
|
417
|
+
: {})}
|
|
390
418
|
/>
|
|
391
419
|
);
|
|
392
420
|
}
|
|
@@ -404,6 +432,8 @@ export function createNode(
|
|
|
404
432
|
collidedPoint: [number, number, number],
|
|
405
433
|
collidedNormal: [number, number, number]
|
|
406
434
|
) => void,
|
|
435
|
+
isDragActive?: (assetId: string) => boolean,
|
|
436
|
+
notifyPhysicsDrag?: (assetId: string) => void,
|
|
407
437
|
onSceneChange?: (sceneId: string, sceneName: string) => void,
|
|
408
438
|
runtimeCtx?: SequenceRuntimeContext
|
|
409
439
|
): React.ReactElement | null {
|
|
@@ -415,6 +445,7 @@ export function createNode(
|
|
|
415
445
|
scene,
|
|
416
446
|
onAnimationTrigger,
|
|
417
447
|
animationStates,
|
|
448
|
+
isDragActive,
|
|
418
449
|
onSceneChange,
|
|
419
450
|
runtimeCtx
|
|
420
451
|
);
|
|
@@ -422,16 +453,24 @@ export function createNode(
|
|
|
422
453
|
let node: React.ReactElement | null;
|
|
423
454
|
switch (type) {
|
|
424
455
|
case "3D-MODEL":
|
|
425
|
-
|
|
456
|
+
// NOTE: notifyPhysicsDrag and onCollision are distinct wirings — keep both;
|
|
457
|
+
// a drag-only merge here once silently killed collisions.
|
|
458
|
+
node = create3DObject(
|
|
459
|
+
asset,
|
|
460
|
+
config,
|
|
461
|
+
onAssetLoaded,
|
|
462
|
+
notifyPhysicsDrag,
|
|
463
|
+
onCollision
|
|
464
|
+
);
|
|
426
465
|
break;
|
|
427
466
|
case "IMAGE":
|
|
428
|
-
node = createImage(asset, config, onAssetLoaded);
|
|
467
|
+
node = createImage(asset, config, onAssetLoaded, notifyPhysicsDrag);
|
|
429
468
|
break;
|
|
430
469
|
case "TEXT":
|
|
431
|
-
node = createText(asset, config, runtimeCtx?.variableStore);
|
|
470
|
+
node = createText(asset, config, notifyPhysicsDrag, runtimeCtx?.variableStore);
|
|
432
471
|
break;
|
|
433
472
|
case "VIDEO":
|
|
434
|
-
node = createVideo(asset, config);
|
|
473
|
+
node = createVideo(asset, config, notifyPhysicsDrag);
|
|
435
474
|
break;
|
|
436
475
|
default:
|
|
437
476
|
console.warn(`[Studio] Unknown asset type "${type}" for "${asset.name}"`);
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
export { StudioARScene } from "./StudioARScene";
|
|
2
2
|
export { StudioSceneNavigator } from "./StudioSceneNavigator";
|
|
3
|
+
export type {
|
|
4
|
+
StudioSceneNavigatorHandle,
|
|
5
|
+
StudioSceneNavigatorProps,
|
|
6
|
+
} from "./StudioSceneNavigator";
|
|
3
7
|
export type {
|
|
4
8
|
StudioAnimation,
|
|
5
9
|
StudioAsset,
|
|
@@ -149,7 +149,7 @@ export interface StudioApiRequestOutcome {
|
|
|
149
149
|
*/
|
|
150
150
|
export type StudioApiRequestExecutor = (
|
|
151
151
|
functionId: string,
|
|
152
|
-
variables: Record<string, boolean | number | string
|
|
152
|
+
variables: Record<string, boolean | number | string>
|
|
153
153
|
) => Promise<StudioApiRequestOutcome>;
|
|
154
154
|
|
|
155
155
|
export interface StudioSceneFunction {
|
|
@@ -267,13 +267,7 @@ export interface StudioAnimation {
|
|
|
267
267
|
properties: Record<string, unknown>; // Viro keyframe format
|
|
268
268
|
duration_ms: number | null;
|
|
269
269
|
delay_ms: number | null;
|
|
270
|
-
easing:
|
|
271
|
-
| "Linear"
|
|
272
|
-
| "EaseIn"
|
|
273
|
-
| "EaseOut"
|
|
274
|
-
| "EaseInEaseOut"
|
|
275
|
-
| "Bounce"
|
|
276
|
-
| null;
|
|
270
|
+
easing: "Linear" | "EaseIn" | "EaseOut" | "EaseInEaseOut" | "Bounce" | null;
|
|
277
271
|
loop: boolean;
|
|
278
272
|
interruptible: boolean;
|
|
279
273
|
on_start_function: string | null;
|
|
@@ -326,6 +320,7 @@ export interface StudioSceneResponse {
|
|
|
326
320
|
functions: StudioSceneFunction[];
|
|
327
321
|
/** Absent in responses from backends predating the Variables feature. */
|
|
328
322
|
variables?: StudioSceneVariable[];
|
|
323
|
+
is_free_tier?: boolean;
|
|
329
324
|
meta: { request_id: string };
|
|
330
325
|
}
|
|
331
326
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VIRO_VERSION = "2.57.
|
|
1
|
+
export const VIRO_VERSION = "2.57.4";
|
|
@@ -75,6 +75,8 @@ type Props = ViewProps & {
|
|
|
75
75
|
autofocus?: boolean;
|
|
76
76
|
videoQuality?: "High" | "Low";
|
|
77
77
|
numberOfTrackedImages?: number;
|
|
78
|
+
/** AR depth/people occlusion. Flows via ...rest to ViroARSceneNavigator. */
|
|
79
|
+
occlusionMode?: "peopleOnly" | "depthBased";
|
|
78
80
|
|
|
79
81
|
// ── Forwarded to ViroVRSceneNavigator (Quest path via bridge) ──────────────
|
|
80
82
|
vrModeEnabled?: boolean;
|