mujoco-react 9.1.0 → 9.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +121 -17
- package/dist/{chunk-33CV6HSV.js → chunk-T3GVZJ4F.js} +222 -8
- package/dist/chunk-T3GVZJ4F.js.map +1 -0
- package/dist/index.d.ts +198 -6
- package/dist/index.js +1109 -216
- package/dist/index.js.map +1 -1
- package/dist/spark.d.ts +24 -2
- package/dist/spark.js +89 -3
- package/dist/spark.js.map +1 -1
- package/dist/{types-C5gTvR7b.d.ts → types-oxbxOkAx.d.ts} +190 -2
- package/dist/vite.d.ts +1 -1
- package/dist/vite.js +6 -3
- package/dist/vite.js.map +1 -1
- package/package.json +1 -1
- package/src/components/VisualScenario.tsx +178 -1
- package/src/core/MujocoSimProvider.tsx +473 -11
- package/src/core/SceneLoader.ts +13 -0
- package/src/core/createController.tsx +6 -2
- package/src/hooks/useCameraFrameCapture.ts +94 -0
- package/src/hooks/useCameraSequenceRecorder.ts +59 -0
- package/src/hooks/useMountedCameraSequenceRecorder.ts +107 -0
- package/src/index.ts +67 -0
- package/src/rendering/cameraFrameCapture.ts +353 -0
- package/src/rendering/cameraFrameSource.ts +375 -0
- package/src/spark.tsx +144 -0
- package/src/types.ts +212 -2
- package/src/vite.ts +5 -2
- package/dist/chunk-33CV6HSV.js.map +0 -1
package/README.md
CHANGED
|
@@ -165,11 +165,24 @@ visual scenarios as data, pass the scenario directly; the component resolves the
|
|
|
165
165
|
splat asset and paired MJCF collision proxy metadata for you.
|
|
166
166
|
|
|
167
167
|
```tsx
|
|
168
|
-
import { SplatEnvironment,
|
|
168
|
+
import { MujocoCanvas, SplatEnvironment, useSplatSceneConfig } from "mujoco-react";
|
|
169
169
|
|
|
170
|
-
|
|
170
|
+
const splat = useSplatSceneConfig({ sceneConfig, scenario });
|
|
171
|
+
|
|
172
|
+
<MujocoCanvas config={splat.sceneConfig}>
|
|
173
|
+
{splat.environment ? (
|
|
174
|
+
<SplatEnvironment environment={splat.environment} renderer="custom">
|
|
175
|
+
<MySplatRenderer src={splat.environment.splat.src} />
|
|
176
|
+
</SplatEnvironment>
|
|
177
|
+
) : null}
|
|
178
|
+
</MujocoCanvas>;
|
|
171
179
|
```
|
|
172
180
|
|
|
181
|
+
Use `splat.readiness` or `getSplatEnvironmentReadiness(scenario)` to gate
|
|
182
|
+
authoring and import flows. The status distinguishes disabled scenarios,
|
|
183
|
+
missing splat assets, missing MJCF collision proxies, unsupported Spark formats,
|
|
184
|
+
and ready paired environments.
|
|
185
|
+
|
|
173
186
|
For MuJoCo + 3DGS composition, derive the collision environment from the same
|
|
174
187
|
splat metadata and pass the resulting config to `<MujocoCanvas>`:
|
|
175
188
|
|
|
@@ -192,16 +205,18 @@ npm install @sparkjsdev/spark
|
|
|
192
205
|
```tsx
|
|
193
206
|
import {
|
|
194
207
|
SparkSplatEnvironment,
|
|
195
|
-
|
|
208
|
+
useSparkSplatEnvironment,
|
|
196
209
|
} from "mujoco-react/spark";
|
|
197
210
|
|
|
198
211
|
function Scene() {
|
|
199
|
-
const splat =
|
|
212
|
+
const splat = useSparkSplatEnvironment({ sceneConfig, scenario });
|
|
200
213
|
|
|
201
214
|
return (
|
|
202
|
-
<MujocoCanvas config={sceneConfig} gl={{ preserveDrawingBuffer: true }}>
|
|
203
|
-
|
|
204
|
-
|
|
215
|
+
<MujocoCanvas config={splat.sceneConfig} gl={{ preserveDrawingBuffer: true }}>
|
|
216
|
+
{splat.environment ? (
|
|
217
|
+
<SparkSplatEnvironment hideGroundMeshes {...splat.props} />
|
|
218
|
+
) : null}
|
|
219
|
+
<StatusBadge status={splat.lifecycle.status} error={splat.lifecycle.error} />
|
|
205
220
|
</MujocoCanvas>
|
|
206
221
|
);
|
|
207
222
|
}
|
|
@@ -213,18 +228,20 @@ function Scene() {
|
|
|
213
228
|
## Write Controllers
|
|
214
229
|
|
|
215
230
|
```tsx
|
|
216
|
-
import { useBeforePhysicsStep } from "mujoco-react";
|
|
231
|
+
import { RobotActuators, useBeforePhysicsStep, useCtrl } from "mujoco-react";
|
|
217
232
|
|
|
218
233
|
function MyController() {
|
|
234
|
+
const shoulder = useCtrl(RobotActuators.franka.actuator1);
|
|
235
|
+
|
|
219
236
|
useBeforePhysicsStep(({ data }) => {
|
|
220
|
-
|
|
237
|
+
shoulder.write(Math.sin(data.time));
|
|
221
238
|
});
|
|
222
239
|
|
|
223
240
|
return null;
|
|
224
241
|
}
|
|
225
242
|
```
|
|
226
243
|
|
|
227
|
-
Controllers are just React children that read sensors, write
|
|
244
|
+
Controllers are just React children that read sensors, write named controls, apply forces, or call the `MujocoSimAPI` at physics-step time.
|
|
228
245
|
|
|
229
246
|
With generated resource values, reusable controllers can be scoped to one robot without hand-typing names:
|
|
230
247
|
|
|
@@ -388,15 +405,20 @@ function useWebSocketControls(url: string) {
|
|
|
388
405
|
For reusable controllers with typed config, default merging, and children, use the `createController` factory:
|
|
389
406
|
|
|
390
407
|
```tsx
|
|
391
|
-
import {
|
|
408
|
+
import {
|
|
409
|
+
RobotActuators,
|
|
410
|
+
createController,
|
|
411
|
+
useBeforePhysicsStep,
|
|
412
|
+
useCtrl,
|
|
413
|
+
} from "mujoco-react";
|
|
392
414
|
|
|
393
415
|
export const MyController = createController<{ gain: number }>(
|
|
394
416
|
{ name: "MyController", defaultConfig: { gain: 1.0 } },
|
|
395
417
|
({ config, children }) => {
|
|
396
|
-
const shoulder = useCtrl(
|
|
418
|
+
const shoulder = useCtrl(RobotActuators.franka.actuator1);
|
|
397
419
|
|
|
398
|
-
useBeforePhysicsStep(() => {
|
|
399
|
-
shoulder.write(config.gain * Math.sin(
|
|
420
|
+
useBeforePhysicsStep(({ data }) => {
|
|
421
|
+
shoulder.write(config.gain * Math.sin(data.time));
|
|
400
422
|
});
|
|
401
423
|
|
|
402
424
|
return <>{children}</>;
|
|
@@ -826,8 +848,12 @@ if (mujoco) {
|
|
|
826
848
|
Run logic **before** `mj_step` each frame. Write to `data.ctrl`, apply forces, drive automation.
|
|
827
849
|
|
|
828
850
|
```tsx
|
|
851
|
+
import { RobotActuators, useBeforePhysicsStep, useCtrl } from "mujoco-react";
|
|
852
|
+
|
|
853
|
+
const shoulder = useCtrl(RobotActuators.franka.actuator1);
|
|
854
|
+
|
|
829
855
|
useBeforePhysicsStep(({ data }) => {
|
|
830
|
-
|
|
856
|
+
shoulder.write(Math.sin(data.time));
|
|
831
857
|
});
|
|
832
858
|
```
|
|
833
859
|
|
|
@@ -861,8 +887,8 @@ Read sensor values by name. Returns a `SensorHandle` with `read()`, `dim`, and `
|
|
|
861
887
|
```tsx
|
|
862
888
|
import { RobotSensors, useSensor } from "mujoco-react";
|
|
863
889
|
|
|
864
|
-
const
|
|
865
|
-
//
|
|
890
|
+
const imu = useSensor(RobotSensors.g1["imu-torso-angular-velocity"]);
|
|
891
|
+
// imu.read() -> Float64Array, imu.dim -> number
|
|
866
892
|
```
|
|
867
893
|
|
|
868
894
|
### `useBodyState(name)`
|
|
@@ -997,6 +1023,84 @@ const blob = await apiRef.current?.captureFrameBlob({ type: "image/png" });
|
|
|
997
1023
|
Use `useFrameCapture()` or the standalone `captureFrame()` helpers when you own
|
|
998
1024
|
the canvas or want to capture a custom container.
|
|
999
1025
|
|
|
1026
|
+
Use `captureCameraFrame()` / `captureCameraFrameBlob()` when dataset generation
|
|
1027
|
+
needs an offscreen camera render at a stable resolution without moving the
|
|
1028
|
+
user's interactive viewport. Pass `cameraName`, `siteName`, or `bodyName` to
|
|
1029
|
+
record true MuJoCo-mounted camera frames; the returned image includes
|
|
1030
|
+
`source.kind` so dataset pipelines can reject fallback or synthetic fixed poses.
|
|
1031
|
+
|
|
1032
|
+
Use `recordCameraSequence()` / `useCameraSequenceRecorder()` to step policy
|
|
1033
|
+
rollouts and capture synchronized per-camera frames from one or more MuJoCo
|
|
1034
|
+
camera configs. Sequence recording requires mounted MuJoCo camera, site, or
|
|
1035
|
+
body selectors by default; use still capture APIs for synthetic debug poses.
|
|
1036
|
+
|
|
1037
|
+
For LeRobot-style datasets, prefer the named-camera wrapper. It resolves task
|
|
1038
|
+
camera keys to MuJoCo cameras/sites/bodies, records the sequence, and returns
|
|
1039
|
+
the plan and readiness summary alongside frame provenance:
|
|
1040
|
+
|
|
1041
|
+
```tsx
|
|
1042
|
+
const sequence = await recordMountedCameraFrameSequence(api, {
|
|
1043
|
+
cameraKeys: ["head", "left_wrist", "right_wrist"],
|
|
1044
|
+
aliases: {
|
|
1045
|
+
head: [{ siteName: "head_camera_rgb_optical_frame" }],
|
|
1046
|
+
left_wrist: [{ siteName: "left_wrist_camera_optical_frame" }],
|
|
1047
|
+
right_wrist: [{ siteName: "right_wrist_camera_optical_frame" }],
|
|
1048
|
+
},
|
|
1049
|
+
defaults: {
|
|
1050
|
+
width: 640,
|
|
1051
|
+
height: 480,
|
|
1052
|
+
type: "image/png",
|
|
1053
|
+
fov: 45,
|
|
1054
|
+
near: 0.01,
|
|
1055
|
+
far: 100,
|
|
1056
|
+
},
|
|
1057
|
+
frames: 16,
|
|
1058
|
+
stepsPerFrame: 1,
|
|
1059
|
+
retainFrames: false,
|
|
1060
|
+
requireMountedSources: true,
|
|
1061
|
+
onFrame: ({ frameIndex, cameras }) => {
|
|
1062
|
+
queueLeRobotImages(frameIndex, cameras);
|
|
1063
|
+
},
|
|
1064
|
+
});
|
|
1065
|
+
|
|
1066
|
+
sequence.readiness.ready; // true when every requested stream resolved
|
|
1067
|
+
sequence.plan.missingKeys; // unresolved task cameras, if requireAll is false
|
|
1068
|
+
sequence.cameraSummaries.head.source; // mounted source provenance
|
|
1069
|
+
```
|
|
1070
|
+
|
|
1071
|
+
`recordMountedCameraFrameSequence()` requires all requested `cameraKeys` by
|
|
1072
|
+
default so dataset recording cannot silently omit a camera stream. Set
|
|
1073
|
+
`requireAll: false` only for exploratory tooling that can tolerate partial
|
|
1074
|
+
camera coverage.
|
|
1075
|
+
|
|
1076
|
+
Inside `<MujocoCanvas>` children, `useMountedCameraSequenceRecorder()` exposes
|
|
1077
|
+
the same planning and recording surface with React status/error state.
|
|
1078
|
+
|
|
1079
|
+
Use `resolveMountedCameraFrameSource()` when dataset feature names need to map
|
|
1080
|
+
to named MuJoCo cameras, sites, or bodies before recording. The helper accepts
|
|
1081
|
+
the model resource lists plus app-level aliases and returns both the capture
|
|
1082
|
+
selector and the mounted-source provenance that should be stored beside the
|
|
1083
|
+
dataset:
|
|
1084
|
+
|
|
1085
|
+
```tsx
|
|
1086
|
+
const resolved = resolveMountedCameraFrameSource("head", {
|
|
1087
|
+
cameras: api.getCameras(),
|
|
1088
|
+
sites: api.getSites(),
|
|
1089
|
+
bodies: api.getBodies(),
|
|
1090
|
+
aliases: {
|
|
1091
|
+
head: [{ siteName: "head_camera_rgb_optical_frame" }],
|
|
1092
|
+
},
|
|
1093
|
+
});
|
|
1094
|
+
|
|
1095
|
+
if (!resolved) throw new Error("head does not resolve to a MuJoCo source");
|
|
1096
|
+
|
|
1097
|
+
await api.recordCameraSequence({
|
|
1098
|
+
frames: 16,
|
|
1099
|
+
requireMountedSources: true,
|
|
1100
|
+
cameras: [{ key: "head", width: 640, height: 480, ...resolved.selector }],
|
|
1101
|
+
});
|
|
1102
|
+
```
|
|
1103
|
+
|
|
1000
1104
|
### `useCtrlNoise(config)`
|
|
1001
1105
|
|
|
1002
1106
|
Apply Gaussian noise to controls for robustness testing:
|
|
@@ -3,7 +3,88 @@ import { useEffect, useMemo } from 'react';
|
|
|
3
3
|
import * as THREE from 'three';
|
|
4
4
|
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
5
5
|
|
|
6
|
-
// src/
|
|
6
|
+
// src/types.ts
|
|
7
|
+
var runtimeRobotResources = {};
|
|
8
|
+
var REGISTER_RESOURCE_KEYS = ["actuators", "sensors", "bodies", "joints", "sites", "geoms", "keyframes", "cameras"];
|
|
9
|
+
function createEmptyRuntimeResources() {
|
|
10
|
+
return {
|
|
11
|
+
actuators: {},
|
|
12
|
+
sensors: {},
|
|
13
|
+
bodies: {},
|
|
14
|
+
joints: {},
|
|
15
|
+
sites: {},
|
|
16
|
+
geoms: {},
|
|
17
|
+
keyframes: {},
|
|
18
|
+
cameras: {}
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function registerRobotResources(resources) {
|
|
22
|
+
for (const [robot, robotResources] of Object.entries(resources)) {
|
|
23
|
+
const existing = runtimeRobotResources[robot] ?? createEmptyRuntimeResources();
|
|
24
|
+
for (const key of REGISTER_RESOURCE_KEYS) {
|
|
25
|
+
existing[key] = { ...existing[key], ...robotResources[key] ?? {} };
|
|
26
|
+
}
|
|
27
|
+
runtimeRobotResources[robot] = existing;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function createResourceCategory(key) {
|
|
31
|
+
return new Proxy({}, {
|
|
32
|
+
get(_target, robot) {
|
|
33
|
+
if (typeof robot !== "string") return void 0;
|
|
34
|
+
return runtimeRobotResources[robot]?.[key] ?? {};
|
|
35
|
+
},
|
|
36
|
+
ownKeys() {
|
|
37
|
+
return Reflect.ownKeys(runtimeRobotResources);
|
|
38
|
+
},
|
|
39
|
+
getOwnPropertyDescriptor(_target, robot) {
|
|
40
|
+
if (typeof robot !== "string" || !(robot in runtimeRobotResources)) return void 0;
|
|
41
|
+
return { enumerable: true, configurable: true };
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
var RobotResources = new Proxy(runtimeRobotResources, {
|
|
46
|
+
get(target, robot) {
|
|
47
|
+
if (typeof robot !== "string") return void 0;
|
|
48
|
+
return target[robot] ?? createEmptyRuntimeResources();
|
|
49
|
+
},
|
|
50
|
+
ownKeys(target) {
|
|
51
|
+
return Reflect.ownKeys(target);
|
|
52
|
+
},
|
|
53
|
+
getOwnPropertyDescriptor(target, robot) {
|
|
54
|
+
if (typeof robot !== "string" || !(robot in target)) return void 0;
|
|
55
|
+
return { enumerable: true, configurable: true };
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
var RobotActuators = createResourceCategory("actuators");
|
|
59
|
+
var RobotSensors = createResourceCategory("sensors");
|
|
60
|
+
var RobotBodies = createResourceCategory("bodies");
|
|
61
|
+
var RobotJoints = createResourceCategory("joints");
|
|
62
|
+
var RobotSites = createResourceCategory("sites");
|
|
63
|
+
var RobotGeoms = createResourceCategory("geoms");
|
|
64
|
+
var RobotKeyframes = createResourceCategory("keyframes");
|
|
65
|
+
var RobotCameras = createResourceCategory("cameras");
|
|
66
|
+
function getContact(contacts, i) {
|
|
67
|
+
try {
|
|
68
|
+
return contacts.get(i);
|
|
69
|
+
} catch {
|
|
70
|
+
return void 0;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function withContacts(data, read) {
|
|
74
|
+
const contacts = data.contact;
|
|
75
|
+
try {
|
|
76
|
+
return read(contacts);
|
|
77
|
+
} finally {
|
|
78
|
+
contacts.delete?.();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
var SplatEnvironmentReadinessStatus = {
|
|
82
|
+
Disabled: "disabled",
|
|
83
|
+
MissingSplat: "missing-splat",
|
|
84
|
+
MissingCollisionProxy: "missing-collision-proxy",
|
|
85
|
+
UnsupportedFormat: "unsupported-format",
|
|
86
|
+
Ready: "ready"
|
|
87
|
+
};
|
|
7
88
|
var DEFAULT_BACKGROUND = "#181a1f";
|
|
8
89
|
function ScenarioLighting({
|
|
9
90
|
preset = "studio",
|
|
@@ -205,21 +286,151 @@ function useSplatEnvironment({
|
|
|
205
286
|
const resolvedSrc = src ?? scenarioEnvironment?.splat.src ?? scenario?.splat?.src;
|
|
206
287
|
const resolvedFormat = format ?? scenarioEnvironment?.splat.format ?? scenario?.splat?.format ?? "spz";
|
|
207
288
|
const resolvedCollisionProxy = collisionProxy ?? scenarioEnvironment?.collisionProxy ?? scenario?.splat?.collisionProxy ?? void 0;
|
|
289
|
+
const readiness = useMemo(
|
|
290
|
+
() => getSplatEnvironmentReadiness({
|
|
291
|
+
environment: scenarioEnvironment,
|
|
292
|
+
scenario,
|
|
293
|
+
renderer,
|
|
294
|
+
src: resolvedSrc,
|
|
295
|
+
format: resolvedFormat,
|
|
296
|
+
collisionProxy: resolvedCollisionProxy
|
|
297
|
+
}),
|
|
298
|
+
[
|
|
299
|
+
collisionProxy,
|
|
300
|
+
renderer,
|
|
301
|
+
resolvedCollisionProxy,
|
|
302
|
+
resolvedFormat,
|
|
303
|
+
resolvedSrc,
|
|
304
|
+
scenario,
|
|
305
|
+
scenarioEnvironment
|
|
306
|
+
]
|
|
307
|
+
);
|
|
208
308
|
return useMemo(
|
|
209
309
|
() => ({
|
|
210
310
|
src: resolvedSrc,
|
|
211
311
|
format: resolvedFormat,
|
|
212
312
|
collisionProxy: resolvedCollisionProxy,
|
|
313
|
+
readiness,
|
|
213
314
|
userData: createSplatEnvironmentUserData({
|
|
214
315
|
environment: scenarioEnvironment,
|
|
215
316
|
src: resolvedSrc,
|
|
216
317
|
format: resolvedFormat,
|
|
217
|
-
collisionProxy: resolvedCollisionProxy
|
|
318
|
+
collisionProxy: resolvedCollisionProxy,
|
|
319
|
+
readiness
|
|
218
320
|
})
|
|
219
321
|
}),
|
|
220
|
-
[
|
|
322
|
+
[
|
|
323
|
+
scenarioEnvironment,
|
|
324
|
+
resolvedSrc,
|
|
325
|
+
resolvedFormat,
|
|
326
|
+
resolvedCollisionProxy,
|
|
327
|
+
readiness
|
|
328
|
+
]
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
function useSplatSceneConfig({
|
|
332
|
+
sceneConfig,
|
|
333
|
+
scenario,
|
|
334
|
+
environment,
|
|
335
|
+
enabled = true,
|
|
336
|
+
renderer
|
|
337
|
+
}) {
|
|
338
|
+
const resolvedEnvironment = useMemo(
|
|
339
|
+
() => enabled ? environment ?? (scenario ? createPairedSplatEnvironment(scenario, { renderer }) : void 0) : void 0,
|
|
340
|
+
[enabled, environment, renderer, scenario]
|
|
341
|
+
);
|
|
342
|
+
const readiness = useMemo(
|
|
343
|
+
() => getSplatEnvironmentReadiness({
|
|
344
|
+
environment: resolvedEnvironment,
|
|
345
|
+
scenario,
|
|
346
|
+
renderer,
|
|
347
|
+
enabled
|
|
348
|
+
}),
|
|
349
|
+
[enabled, renderer, resolvedEnvironment, scenario]
|
|
350
|
+
);
|
|
351
|
+
const resolvedSceneConfig = useMemo(
|
|
352
|
+
() => resolvedEnvironment ? withSplatEnvironment(sceneConfig, resolvedEnvironment) : sceneConfig,
|
|
353
|
+
[resolvedEnvironment, sceneConfig]
|
|
354
|
+
);
|
|
355
|
+
return useMemo(
|
|
356
|
+
() => ({
|
|
357
|
+
environment: resolvedEnvironment,
|
|
358
|
+
sceneConfig: resolvedSceneConfig,
|
|
359
|
+
enabled: enabled && readiness.status !== SplatEnvironmentReadinessStatus.Disabled,
|
|
360
|
+
readiness
|
|
361
|
+
}),
|
|
362
|
+
[enabled, readiness, resolvedEnvironment, resolvedSceneConfig]
|
|
221
363
|
);
|
|
222
364
|
}
|
|
365
|
+
function getSplatEnvironmentReadiness({
|
|
366
|
+
environment,
|
|
367
|
+
scenario,
|
|
368
|
+
renderer,
|
|
369
|
+
src,
|
|
370
|
+
format,
|
|
371
|
+
collisionProxy,
|
|
372
|
+
enabled = true
|
|
373
|
+
}) {
|
|
374
|
+
const splat = scenario?.splat;
|
|
375
|
+
const resolvedSrc = src ?? environment?.splat.src ?? splat?.src;
|
|
376
|
+
const resolvedFormat = format ?? environment?.splat.format ?? splat?.format ?? "spz";
|
|
377
|
+
const resolvedRenderer = renderer ?? environment?.splat.renderer;
|
|
378
|
+
const resolvedCollisionProxy = collisionProxy ?? environment?.collisionProxy ?? splat?.collisionProxy ?? void 0;
|
|
379
|
+
const requiresCollisionProxy = splat?.requiresCollisionProxy ?? true;
|
|
380
|
+
if (!enabled || splat && splat.enabled === false && !environment) {
|
|
381
|
+
return {
|
|
382
|
+
status: SplatEnvironmentReadinessStatus.Disabled,
|
|
383
|
+
ready: false,
|
|
384
|
+
requiresCollisionProxy,
|
|
385
|
+
missing: [],
|
|
386
|
+
format: resolvedFormat,
|
|
387
|
+
renderer: resolvedRenderer,
|
|
388
|
+
message: "Splat environment is disabled."
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
if (!resolvedSrc) {
|
|
392
|
+
return {
|
|
393
|
+
status: SplatEnvironmentReadinessStatus.MissingSplat,
|
|
394
|
+
ready: false,
|
|
395
|
+
requiresCollisionProxy,
|
|
396
|
+
missing: ["splat"],
|
|
397
|
+
format: resolvedFormat,
|
|
398
|
+
renderer: resolvedRenderer,
|
|
399
|
+
message: "Splat environment is missing a visual asset source."
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
if (resolvedRenderer === "spark" && resolvedFormat !== "spz") {
|
|
403
|
+
return {
|
|
404
|
+
status: SplatEnvironmentReadinessStatus.UnsupportedFormat,
|
|
405
|
+
ready: false,
|
|
406
|
+
requiresCollisionProxy,
|
|
407
|
+
missing: [],
|
|
408
|
+
format: resolvedFormat,
|
|
409
|
+
renderer: resolvedRenderer,
|
|
410
|
+
message: `Spark splat rendering requires .spz assets; received ${resolvedFormat}.`
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
if (requiresCollisionProxy && !resolvedCollisionProxy?.xmlPath) {
|
|
414
|
+
return {
|
|
415
|
+
status: SplatEnvironmentReadinessStatus.MissingCollisionProxy,
|
|
416
|
+
ready: false,
|
|
417
|
+
requiresCollisionProxy,
|
|
418
|
+
missing: ["collisionProxy"],
|
|
419
|
+
format: resolvedFormat,
|
|
420
|
+
renderer: resolvedRenderer,
|
|
421
|
+
message: "Splat environment is missing paired MJCF collision proxy XML."
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
return {
|
|
425
|
+
status: SplatEnvironmentReadinessStatus.Ready,
|
|
426
|
+
ready: true,
|
|
427
|
+
requiresCollisionProxy,
|
|
428
|
+
missing: [],
|
|
429
|
+
format: resolvedFormat,
|
|
430
|
+
renderer: resolvedRenderer,
|
|
431
|
+
message: requiresCollisionProxy ? "Splat environment has visual asset and collision proxy metadata." : "Splat environment has a visual asset and does not require collision proxy metadata."
|
|
432
|
+
};
|
|
433
|
+
}
|
|
223
434
|
function createPairedSplatEnvironment(scenario, options = {}) {
|
|
224
435
|
const splat = scenario.splat;
|
|
225
436
|
const collisionProxy = splat?.collisionProxy;
|
|
@@ -277,7 +488,8 @@ function createSplatEnvironmentUserData({
|
|
|
277
488
|
environment,
|
|
278
489
|
src,
|
|
279
490
|
format = "spz",
|
|
280
|
-
collisionProxy
|
|
491
|
+
collisionProxy,
|
|
492
|
+
readiness
|
|
281
493
|
}) {
|
|
282
494
|
return {
|
|
283
495
|
role: "splat-environment",
|
|
@@ -288,7 +500,9 @@ function createSplatEnvironmentUserData({
|
|
|
288
500
|
splatRenderer: environment?.splat.renderer,
|
|
289
501
|
collisionProxyStatus: collisionProxy?.status ?? "missing",
|
|
290
502
|
collisionProxyXmlPath: collisionProxy?.xmlPath,
|
|
291
|
-
collisionProxyPrimitives: collisionProxy?.primitives ?? []
|
|
503
|
+
collisionProxyPrimitives: collisionProxy?.primitives ?? [],
|
|
504
|
+
readinessStatus: readiness?.status,
|
|
505
|
+
readinessMessage: readiness?.message
|
|
292
506
|
};
|
|
293
507
|
}
|
|
294
508
|
function createSparkSplatViewerUrl({
|
|
@@ -395,6 +609,6 @@ function clamp01(value) {
|
|
|
395
609
|
* SPDX-License-Identifier: Apache-2.0
|
|
396
610
|
*/
|
|
397
611
|
|
|
398
|
-
export { ScenarioLighting, SplatEnvironment, VisualScenarioEffects, createPairedSplatEnvironment, createSparkSplatViewerUrl, createSplatEnvironmentUserData, getScenarioBackground, getScenarioCameraPosition, useSplatEnvironment, useVisualScenarioEffects, withSplatEnvironment };
|
|
399
|
-
//# sourceMappingURL=chunk-
|
|
400
|
-
//# sourceMappingURL=chunk-
|
|
612
|
+
export { RobotActuators, RobotBodies, RobotCameras, RobotGeoms, RobotJoints, RobotKeyframes, RobotResources, RobotSensors, RobotSites, ScenarioLighting, SplatEnvironment, SplatEnvironmentReadinessStatus, VisualScenarioEffects, createPairedSplatEnvironment, createSparkSplatViewerUrl, createSplatEnvironmentUserData, getContact, getScenarioBackground, getScenarioCameraPosition, getSplatEnvironmentReadiness, registerRobotResources, useSplatEnvironment, useSplatSceneConfig, useVisualScenarioEffects, withContacts, withSplatEnvironment };
|
|
613
|
+
//# sourceMappingURL=chunk-T3GVZJ4F.js.map
|
|
614
|
+
//# sourceMappingURL=chunk-T3GVZJ4F.js.map
|