mujoco-react 8.10.0 → 8.11.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 CHANGED
@@ -145,20 +145,42 @@ Use it as a child of `<MujocoCanvas>`:
145
145
 
146
146
  Gaussian splats are visual context; MuJoCo XML remains the source of physics, contacts, and task fixtures. Pair each splat asset with collision proxy metadata so scene variants, rollouts, and datasets preserve both sides of the environment.
147
147
 
148
- Use the renderer-agnostic boundary from the main package:
148
+ Use `VisualScenarioEffects` when the same MuJoCo task should render under
149
+ different camera exposure, fog/background, and deterministic material variants:
149
150
 
150
151
  ```tsx
151
- import { SplatEnvironment } from "mujoco-react";
152
-
153
- <SplatEnvironment
154
- src="/models/lab/scene.spz"
155
- format="spz"
156
- collisionProxyMetadata={{
157
- xmlPath: "/models/lab/collision.xml",
158
- status: "validated",
159
- primitives: ["plane", "box"],
160
- }}
161
- />;
152
+ import { ScenarioLighting, VisualScenarioEffects } from "mujoco-react";
153
+
154
+ <MujocoCanvas config={sceneConfig}>
155
+ <VisualScenarioEffects
156
+ scenario={scenario}
157
+ materialFilter={(object) => object.name.startsWith("prop_")}
158
+ />
159
+ <ScenarioLighting preset={scenario.lighting} />
160
+ </MujocoCanvas>;
161
+ ```
162
+
163
+ Use the renderer-agnostic boundary from the main package. If your app stores
164
+ visual scenarios as data, pass the scenario directly; the component resolves the
165
+ splat asset and paired MJCF collision proxy metadata for you.
166
+
167
+ ```tsx
168
+ import { SplatEnvironment, withSplatEnvironment } from "mujoco-react";
169
+
170
+ <SplatEnvironment scenario={scenario} renderer="custom" />;
171
+ ```
172
+
173
+ For MuJoCo + 3DGS composition, derive the collision environment from the same
174
+ splat metadata and pass the resulting config to `<MujocoCanvas>`:
175
+
176
+ ```tsx
177
+ const sceneConfig = withSplatEnvironment(
178
+ {
179
+ src: "/models/xlerobot/",
180
+ sceneFile: "xlerobot.xml",
181
+ },
182
+ kitchenScenario
183
+ );
162
184
  ```
163
185
 
164
186
  For first-class Spark rendering, install Spark and import the optional adapter:
@@ -168,23 +190,26 @@ npm install @sparkjsdev/spark
168
190
  ```
169
191
 
170
192
  ```tsx
171
- import { SparkSplatEnvironment } from "mujoco-react/spark";
172
-
173
- <MujocoCanvas config={sceneConfig} gl={{ preserveDrawingBuffer: true }}>
174
- <SparkSplatEnvironment
175
- src="/models/lab/scene.spz"
176
- format="spz"
177
- collisionProxyMetadata={{
178
- xmlPath: "/models/lab/collision.xml",
179
- status: "validated",
180
- primitives: ["plane", "box"],
181
- }}
182
- hideGroundMeshes
183
- onStatusChange={(status) => console.log(status)}
184
- />
185
- </MujocoCanvas>;
193
+ import {
194
+ SparkSplatEnvironment,
195
+ useSparkSplatLifecycle,
196
+ } from "mujoco-react/spark";
197
+
198
+ function Scene() {
199
+ const splat = useSparkSplatLifecycle();
200
+
201
+ return (
202
+ <MujocoCanvas config={sceneConfig} gl={{ preserveDrawingBuffer: true }}>
203
+ <SparkSplatEnvironment scenario={scenario} hideGroundMeshes {...splat.props} />
204
+ <StatusBadge status={splat.status} error={splat.error} />
205
+ </MujocoCanvas>
206
+ );
207
+ }
186
208
  ```
187
209
 
210
+ `SparkSplatEnvironment` currently renders `.spz` assets. Use the renderer-agnostic
211
+ `SplatEnvironment` for `.ply`/`.splat` metadata or when wiring a different renderer.
212
+
188
213
  ## Write Controllers
189
214
 
190
215
  ```tsx
@@ -497,6 +522,7 @@ interface SceneConfig {
497
522
  src: string; // Base URL for model files
498
523
  sceneFile: string; // Entry XML/URDF file, e.g. "scene.xml"
499
524
  files?: File[]; // Local files for browser upload workflows
525
+ environmentFiles?: string[]; // Static MJCF environment XMLs merged before compile
500
526
  sceneObjects?: SceneObject[]; // Objects injected into scene XML at load time
501
527
  homeJoints?: number[]; // Initial joint positions
502
528
  xmlPatches?: XmlPatch[]; // Patches applied to XML files during loading
@@ -504,6 +530,16 @@ interface SceneConfig {
504
530
  }
505
531
  ```
506
532
 
533
+ Use `environmentFiles` to compose reusable physics/collision layers with a robot model. For Gaussian splat scenes, keep the `.spz` as a parallel visual layer and point `environmentFiles` at the paired MJCF proxy scene:
534
+
535
+ ```tsx
536
+ const kitchenRobot: SceneConfig = {
537
+ src: "/models/xlerobot/",
538
+ sceneFile: "xlerobot.xml",
539
+ environmentFiles: ["splats/tabletop/scene.xml"],
540
+ };
541
+ ```
542
+
507
543
  ### Local Files and URDF
508
544
 
509
545
  Load browser-selected MJCF or URDF files directly. Folder uploads preserve `webkitRelativePath`, and flat uploads fall back to matching referenced mesh/texture assets by basename:
@@ -905,7 +941,8 @@ const obs = useObservation({ qpos: true, qvel: true, projectedGravity: "torso" }
905
941
  const policy = usePolicy({
906
942
  frequency: 50,
907
943
  onObservation: () => obs.readValues(),
908
- onAction: (action, model, data) => applyAction(action, data),
944
+ infer: ({ observation }) => policySession.run(observation),
945
+ onAction: ({ action, data }) => applyAction(action, data),
909
946
  });
910
947
  ```
911
948
 
@@ -0,0 +1,400 @@
1
+ import { useThree } from '@react-three/fiber';
2
+ import { useEffect, useMemo } from 'react';
3
+ import * as THREE from 'three';
4
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
5
+
6
+ // src/components/VisualScenario.tsx
7
+ var DEFAULT_BACKGROUND = "#181a1f";
8
+ function ScenarioLighting({
9
+ preset = "studio",
10
+ castShadow = true,
11
+ intensity = 1
12
+ }) {
13
+ if (preset === "warehouse") {
14
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
15
+ /* @__PURE__ */ jsx("ambientLight", { intensity: 0.18 * intensity }),
16
+ /* @__PURE__ */ jsx(
17
+ "directionalLight",
18
+ {
19
+ position: [3.5, -2, 5],
20
+ intensity: 2.2 * intensity,
21
+ castShadow
22
+ }
23
+ ),
24
+ /* @__PURE__ */ jsx("directionalLight", { position: [-2, 1.5, 2.5], intensity: 0.25 * intensity })
25
+ ] });
26
+ }
27
+ if (preset === "low-light") {
28
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
29
+ /* @__PURE__ */ jsx("ambientLight", { intensity: 0.08 * intensity }),
30
+ /* @__PURE__ */ jsx(
31
+ "directionalLight",
32
+ {
33
+ position: [2, -2, 3],
34
+ intensity: 0.75 * intensity,
35
+ castShadow
36
+ }
37
+ ),
38
+ /* @__PURE__ */ jsx("pointLight", { position: [-0.5, -0.8, 1.3], intensity: 0.6 * intensity })
39
+ ] });
40
+ }
41
+ if (preset === "splat") {
42
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
43
+ /* @__PURE__ */ jsx("ambientLight", { intensity: 0.42 * intensity }),
44
+ /* @__PURE__ */ jsx(
45
+ "directionalLight",
46
+ {
47
+ position: [1.8, -2.4, 3.5],
48
+ intensity: 1.2 * intensity,
49
+ castShadow
50
+ }
51
+ ),
52
+ /* @__PURE__ */ jsx("pointLight", { position: [0.4, 0.2, 1.4], intensity: 0.35 * intensity })
53
+ ] });
54
+ }
55
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
56
+ /* @__PURE__ */ jsx("ambientLight", { intensity: 0.35 * intensity }),
57
+ /* @__PURE__ */ jsx(
58
+ "directionalLight",
59
+ {
60
+ position: [2.5, -3, 4],
61
+ intensity: 1.6 * intensity,
62
+ castShadow
63
+ }
64
+ )
65
+ ] });
66
+ }
67
+ function getScenarioBackground(preset, fallback = DEFAULT_BACKGROUND) {
68
+ if (preset === "warehouse") return "#20242b";
69
+ if (preset === "low-light") return "#0f1115";
70
+ if (preset === "splat") return "#1b1f24";
71
+ return fallback;
72
+ }
73
+ function getScenarioCameraPosition(basePosition, scenario) {
74
+ const [x, y, z] = basePosition;
75
+ const jitter = scenario?.camera?.jitter ?? 0;
76
+ return [
77
+ Number((x + jitter * 0.6).toFixed(3)),
78
+ Number((y - jitter * 0.4).toFixed(3)),
79
+ Number((z + jitter * 0.25).toFixed(3))
80
+ ];
81
+ }
82
+ function VisualScenarioEffects(props) {
83
+ useVisualScenarioEffects(props);
84
+ return null;
85
+ }
86
+ function useVisualScenarioEffects({
87
+ scenario,
88
+ enabled = true,
89
+ applyBackground = true,
90
+ applyFog = true,
91
+ applyRenderer = true,
92
+ applyMaterials = true,
93
+ background,
94
+ fogNear,
95
+ fogFar,
96
+ materialFilter
97
+ }) {
98
+ const { gl, scene, invalidate } = useThree();
99
+ useEffect(() => {
100
+ if (!enabled || !scenario) {
101
+ return void 0;
102
+ }
103
+ const previousExposure = gl.toneMappingExposure;
104
+ const previousBackground = scene.background;
105
+ const previousFog = scene.fog;
106
+ const materialSnapshots = /* @__PURE__ */ new Map();
107
+ if (applyRenderer) {
108
+ gl.toneMappingExposure = scenario.camera?.exposure ?? 1;
109
+ }
110
+ if (applyBackground) {
111
+ scene.background = new THREE.Color(
112
+ background ?? getScenarioBackground(scenario.lighting)
113
+ );
114
+ }
115
+ if (applyFog) {
116
+ scene.fog = createScenarioFog(scenario, background, fogNear, fogFar);
117
+ }
118
+ if (applyMaterials && scenario.materials) {
119
+ applyScenarioMaterials(scene, scenario, materialSnapshots, materialFilter);
120
+ }
121
+ invalidate();
122
+ return () => {
123
+ gl.toneMappingExposure = previousExposure;
124
+ scene.background = previousBackground;
125
+ scene.fog = previousFog;
126
+ for (const [material, snapshot] of materialSnapshots) {
127
+ const mutable = getMutableScenarioMaterial(material);
128
+ if (!mutable) continue;
129
+ if (snapshot.color) mutable.color.copy(snapshot.color);
130
+ if (typeof snapshot.roughness === "number") {
131
+ mutable.roughness = snapshot.roughness;
132
+ }
133
+ if (typeof snapshot.metalness === "number") {
134
+ mutable.metalness = snapshot.metalness;
135
+ }
136
+ mutable.needsUpdate = true;
137
+ }
138
+ invalidate();
139
+ };
140
+ }, [
141
+ applyBackground,
142
+ applyFog,
143
+ applyMaterials,
144
+ applyRenderer,
145
+ background,
146
+ enabled,
147
+ fogFar,
148
+ fogNear,
149
+ gl,
150
+ invalidate,
151
+ materialFilter,
152
+ scenario,
153
+ scene
154
+ ]);
155
+ }
156
+ function SplatEnvironment({
157
+ environment,
158
+ scenario,
159
+ renderer,
160
+ src,
161
+ format,
162
+ collisionProxy,
163
+ collisionProxyMetadata,
164
+ children,
165
+ showPlaceholder = true,
166
+ ...groupProps
167
+ }) {
168
+ const metadata = useSplatEnvironment({
169
+ environment,
170
+ scenario,
171
+ renderer,
172
+ src,
173
+ format,
174
+ collisionProxy: collisionProxyMetadata
175
+ });
176
+ const existingUserData = typeof groupProps.userData === "object" && groupProps.userData !== null ? groupProps.userData : {};
177
+ return /* @__PURE__ */ jsxs(
178
+ "group",
179
+ {
180
+ ...groupProps,
181
+ userData: {
182
+ ...existingUserData,
183
+ ...metadata.userData
184
+ },
185
+ children: [
186
+ children,
187
+ children || !showPlaceholder ? null : /* @__PURE__ */ jsx(SplatPlaceholder, {}),
188
+ collisionProxy
189
+ ]
190
+ }
191
+ );
192
+ }
193
+ function useSplatEnvironment({
194
+ environment,
195
+ scenario,
196
+ renderer,
197
+ src,
198
+ format,
199
+ collisionProxy
200
+ }) {
201
+ const scenarioEnvironment = useMemo(
202
+ () => environment ?? (scenario ? createPairedSplatEnvironment(scenario, { renderer }) : void 0),
203
+ [environment, renderer, scenario]
204
+ );
205
+ const resolvedSrc = src ?? scenarioEnvironment?.splat.src ?? scenario?.splat?.src;
206
+ const resolvedFormat = format ?? scenarioEnvironment?.splat.format ?? scenario?.splat?.format ?? "spz";
207
+ const resolvedCollisionProxy = collisionProxy ?? scenarioEnvironment?.collisionProxy ?? scenario?.splat?.collisionProxy ?? void 0;
208
+ return useMemo(
209
+ () => ({
210
+ src: resolvedSrc,
211
+ format: resolvedFormat,
212
+ collisionProxy: resolvedCollisionProxy,
213
+ userData: createSplatEnvironmentUserData({
214
+ environment: scenarioEnvironment,
215
+ src: resolvedSrc,
216
+ format: resolvedFormat,
217
+ collisionProxy: resolvedCollisionProxy
218
+ })
219
+ }),
220
+ [scenarioEnvironment, resolvedSrc, resolvedFormat, resolvedCollisionProxy]
221
+ );
222
+ }
223
+ function createPairedSplatEnvironment(scenario, options = {}) {
224
+ const splat = scenario.splat;
225
+ const collisionProxy = splat?.collisionProxy;
226
+ if (!splat?.enabled || !splat.src || !collisionProxy?.xmlPath) {
227
+ return void 0;
228
+ }
229
+ return {
230
+ id: options.id ?? scenario.id ?? "splat-environment",
231
+ label: options.label ?? scenario.label ?? "Gaussian splat environment",
232
+ description: options.description ?? (scenario.environment ? `Visual ${scenario.environment} splat paired with MJCF collision proxy.` : void 0),
233
+ splat: {
234
+ src: splat.src,
235
+ format: splat.format ?? "spz",
236
+ renderer: options.renderer
237
+ },
238
+ collisionProxy: {
239
+ ...collisionProxy,
240
+ xmlPath: collisionProxy.xmlPath
241
+ }
242
+ };
243
+ }
244
+ function isPairedSplatEnvironment(input) {
245
+ return !!input && "collisionProxy" in input && "splat" in input;
246
+ }
247
+ function sceneRelativePath(sceneConfig, path) {
248
+ const src = sceneConfig.src;
249
+ if (!src) return path;
250
+ const base = src.endsWith("/") ? src : src + "/";
251
+ if (path.startsWith(base)) return path.slice(base.length);
252
+ return path;
253
+ }
254
+ function uniquePaths(paths) {
255
+ const seen = /* @__PURE__ */ new Set();
256
+ const result = [];
257
+ for (const path of paths) {
258
+ if (seen.has(path)) continue;
259
+ seen.add(path);
260
+ result.push(path);
261
+ }
262
+ return result;
263
+ }
264
+ function withSplatEnvironment(sceneConfig, input, options = {}) {
265
+ const environment = isPairedSplatEnvironment(input) ? input : input ? createPairedSplatEnvironment(input, options) : void 0;
266
+ const xmlPath = environment?.collisionProxy.xmlPath;
267
+ if (!xmlPath) return sceneConfig;
268
+ return {
269
+ ...sceneConfig,
270
+ environmentFiles: uniquePaths([
271
+ ...sceneConfig.environmentFiles ?? [],
272
+ sceneRelativePath(sceneConfig, xmlPath)
273
+ ])
274
+ };
275
+ }
276
+ function createSplatEnvironmentUserData({
277
+ environment,
278
+ src,
279
+ format = "spz",
280
+ collisionProxy
281
+ }) {
282
+ return {
283
+ role: "splat-environment",
284
+ environmentId: environment?.id,
285
+ environmentLabel: environment?.label,
286
+ splatSrc: src,
287
+ splatFormat: format,
288
+ splatRenderer: environment?.splat.renderer,
289
+ collisionProxyStatus: collisionProxy?.status ?? "missing",
290
+ collisionProxyXmlPath: collisionProxy?.xmlPath,
291
+ collisionProxyPrimitives: collisionProxy?.primitives ?? []
292
+ };
293
+ }
294
+ function createSparkSplatViewerUrl({
295
+ viewerUrl,
296
+ splatSrc
297
+ }) {
298
+ const url = new URL(viewerUrl, "http://mujoco-react.local");
299
+ url.searchParams.set("splat", splatSrc);
300
+ return viewerUrl.startsWith("http") ? url.toString() : `${url.pathname}${url.search}`;
301
+ }
302
+ function SplatPlaceholder() {
303
+ return /* @__PURE__ */ jsx("group", { children: /* @__PURE__ */ jsxs("mesh", { position: [0, 0, 1.2], children: [
304
+ /* @__PURE__ */ jsx("boxGeometry", { args: [2.4, 2.4, 2.4] }),
305
+ /* @__PURE__ */ jsx(
306
+ "meshBasicMaterial",
307
+ {
308
+ color: "#8b8b8b",
309
+ transparent: true,
310
+ opacity: 0.06,
311
+ wireframe: true,
312
+ side: THREE.DoubleSide
313
+ }
314
+ )
315
+ ] }) });
316
+ }
317
+ function createScenarioFog(scenario, background, fogNear, fogFar) {
318
+ if (scenario.lighting === "low-light") {
319
+ return new THREE.Fog(
320
+ background ?? getScenarioBackground(scenario.lighting),
321
+ fogNear ?? 2.5,
322
+ fogFar ?? 9
323
+ );
324
+ }
325
+ if (scenario.lighting === "warehouse") {
326
+ return new THREE.Fog(
327
+ background ?? getScenarioBackground(scenario.lighting),
328
+ fogNear ?? 5,
329
+ fogFar ?? 16
330
+ );
331
+ }
332
+ return null;
333
+ }
334
+ function applyScenarioMaterials(scene, scenario, snapshots, materialFilter) {
335
+ const materials = scenario.materials;
336
+ if (!materials) return;
337
+ scene.traverse((object) => {
338
+ if (!(object instanceof THREE.Mesh)) {
339
+ return;
340
+ }
341
+ for (const material of normalizeMaterials(object.material)) {
342
+ const mutable = getMutableScenarioMaterial(material);
343
+ if (!mutable) continue;
344
+ if (materialFilter && !materialFilter(object, material)) continue;
345
+ if (!snapshots.has(material)) {
346
+ snapshots.set(material, {
347
+ color: mutable.color.clone(),
348
+ roughness: mutable.roughness,
349
+ metalness: mutable.metalness
350
+ });
351
+ }
352
+ applyScenarioMaterial(mutable, object, scenario, materials);
353
+ }
354
+ });
355
+ }
356
+ function applyScenarioMaterial(material, object, scenario, materials) {
357
+ const seed = scenario.seed ?? 0;
358
+ const objectKey = `${scenario.id ?? "scenario"}:${object.name}:${material.name}:${seed}`;
359
+ const variation = hashToUnitInterval(objectKey);
360
+ if (materials.randomizeObjectColors) {
361
+ material.color.setHSL(variation, 0.38, 0.42);
362
+ }
363
+ if (materials.randomizeTableMaterial) {
364
+ material.roughness = clamp01(
365
+ materials.roughness ?? 0.35 + variation * 0.45
366
+ );
367
+ material.metalness = clamp01(
368
+ materials.metalness ?? variation * 0.12
369
+ );
370
+ }
371
+ material.needsUpdate = true;
372
+ }
373
+ function normalizeMaterials(material) {
374
+ return Array.isArray(material) ? material : [material];
375
+ }
376
+ function getMutableScenarioMaterial(material) {
377
+ if (material instanceof THREE.MeshStandardMaterial || material instanceof THREE.MeshPhysicalMaterial) {
378
+ return material;
379
+ }
380
+ return null;
381
+ }
382
+ function hashToUnitInterval(value) {
383
+ let hash = 2166136261;
384
+ for (let index = 0; index < value.length; index += 1) {
385
+ hash ^= value.charCodeAt(index);
386
+ hash = Math.imul(hash, 16777619);
387
+ }
388
+ return (hash >>> 0) / 4294967295;
389
+ }
390
+ function clamp01(value) {
391
+ return Math.max(0, Math.min(1, value));
392
+ }
393
+ /**
394
+ * @license
395
+ * SPDX-License-Identifier: Apache-2.0
396
+ */
397
+
398
+ export { ScenarioLighting, SplatEnvironment, VisualScenarioEffects, createPairedSplatEnvironment, createSparkSplatViewerUrl, createSplatEnvironmentUserData, getScenarioBackground, getScenarioCameraPosition, useSplatEnvironment, useVisualScenarioEffects, withSplatEnvironment };
399
+ //# sourceMappingURL=chunk-SEWQULWO.js.map
400
+ //# sourceMappingURL=chunk-SEWQULWO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/VisualScenario.tsx"],"names":[],"mappings":"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","file":"chunk-SEWQULWO.js","sourcesContent":["/**\n * @license\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { useThree } from '@react-three/fiber';\nimport type { ThreeElements } from '@react-three/fiber';\nimport type { ReactNode } from 'react';\nimport { useEffect, useMemo } from 'react';\nimport * as THREE from 'three';\nimport type {\n PairedSplatEnvironmentConfig,\n ScenarioMaterialConfig,\n SceneConfig,\n SplatCollisionProxyConfig,\n SplatEnvironmentMetadata,\n SplatEnvironmentMetadataInput,\n SplatFormat,\n SplatRendererKind,\n SplatSceneInput,\n ScenarioLightingPreset,\n ScenarioLightingProps,\n SplatEnvironmentProps,\n VisualScenarioConfig,\n VisualScenarioEffectsProps,\n} from '../types';\n\nconst DEFAULT_BACKGROUND = '#181a1f';\n\nexport function ScenarioLighting({\n preset = 'studio',\n castShadow = true,\n intensity = 1,\n}: ScenarioLightingProps) {\n if (preset === 'warehouse') {\n return (\n <>\n <ambientLight intensity={0.18 * intensity} />\n <directionalLight\n position={[3.5, -2, 5]}\n intensity={2.2 * intensity}\n castShadow={castShadow}\n />\n <directionalLight position={[-2, 1.5, 2.5]} intensity={0.25 * intensity} />\n </>\n );\n }\n\n if (preset === 'low-light') {\n return (\n <>\n <ambientLight intensity={0.08 * intensity} />\n <directionalLight\n position={[2, -2, 3]}\n intensity={0.75 * intensity}\n castShadow={castShadow}\n />\n <pointLight position={[-0.5, -0.8, 1.3]} intensity={0.6 * intensity} />\n </>\n );\n }\n\n if (preset === 'splat') {\n return (\n <>\n <ambientLight intensity={0.42 * intensity} />\n <directionalLight\n position={[1.8, -2.4, 3.5]}\n intensity={1.2 * intensity}\n castShadow={castShadow}\n />\n <pointLight position={[0.4, 0.2, 1.4]} intensity={0.35 * intensity} />\n </>\n );\n }\n\n return (\n <>\n <ambientLight intensity={0.35 * intensity} />\n <directionalLight\n position={[2.5, -3, 4]}\n intensity={1.6 * intensity}\n castShadow={castShadow}\n />\n </>\n );\n}\n\nexport function getScenarioBackground(\n preset: ScenarioLightingPreset | undefined,\n fallback = DEFAULT_BACKGROUND\n) {\n if (preset === 'warehouse') return '#20242b';\n if (preset === 'low-light') return '#0f1115';\n if (preset === 'splat') return '#1b1f24';\n return fallback;\n}\n\nexport function getScenarioCameraPosition(\n basePosition: readonly [number, number, number],\n scenario?: Pick<VisualScenarioConfig, 'camera'>\n): [number, number, number] {\n const [x, y, z] = basePosition;\n const jitter = scenario?.camera?.jitter ?? 0;\n\n return [\n Number((x + jitter * 0.6).toFixed(3)),\n Number((y - jitter * 0.4).toFixed(3)),\n Number((z + jitter * 0.25).toFixed(3)),\n ];\n}\n\nexport function VisualScenarioEffects(props: VisualScenarioEffectsProps) {\n useVisualScenarioEffects(props);\n return null;\n}\n\nexport function useVisualScenarioEffects({\n scenario,\n enabled = true,\n applyBackground = true,\n applyFog = true,\n applyRenderer = true,\n applyMaterials = true,\n background,\n fogNear,\n fogFar,\n materialFilter,\n}: VisualScenarioEffectsProps) {\n const { gl, scene, invalidate } = useThree();\n\n useEffect(() => {\n if (!enabled || !scenario) {\n return undefined;\n }\n\n const previousExposure = gl.toneMappingExposure;\n const previousBackground = scene.background;\n const previousFog = scene.fog;\n const materialSnapshots = new Map<\n THREE.Material,\n {\n color?: THREE.Color;\n roughness?: number;\n metalness?: number;\n }\n >();\n\n if (applyRenderer) {\n gl.toneMappingExposure = scenario.camera?.exposure ?? 1;\n }\n\n if (applyBackground) {\n scene.background = new THREE.Color(\n background ?? getScenarioBackground(scenario.lighting)\n );\n }\n\n if (applyFog) {\n scene.fog = createScenarioFog(scenario, background, fogNear, fogFar);\n }\n\n if (applyMaterials && scenario.materials) {\n applyScenarioMaterials(scene, scenario, materialSnapshots, materialFilter);\n }\n\n invalidate();\n\n return () => {\n gl.toneMappingExposure = previousExposure;\n scene.background = previousBackground;\n scene.fog = previousFog;\n\n for (const [material, snapshot] of materialSnapshots) {\n const mutable = getMutableScenarioMaterial(material);\n if (!mutable) continue;\n if (snapshot.color) mutable.color.copy(snapshot.color);\n if (typeof snapshot.roughness === 'number') {\n mutable.roughness = snapshot.roughness;\n }\n if (typeof snapshot.metalness === 'number') {\n mutable.metalness = snapshot.metalness;\n }\n mutable.needsUpdate = true;\n }\n\n invalidate();\n };\n }, [\n applyBackground,\n applyFog,\n applyMaterials,\n applyRenderer,\n background,\n enabled,\n fogFar,\n fogNear,\n gl,\n invalidate,\n materialFilter,\n scenario,\n scene,\n ]);\n}\n\n/**\n * Renderer-agnostic Gaussian splat environment boundary.\n *\n * This component intentionally does not import a specific 3DGS renderer. Pass a\n * Spark/GaussianSplats3D object as `children` once the app chooses a renderer,\n * and pass MuJoCo/MJCF collision proxy visuals via `collisionProxy`.\n */\nexport function SplatEnvironment({\n environment,\n scenario,\n renderer,\n src,\n format,\n collisionProxy,\n collisionProxyMetadata,\n children,\n showPlaceholder = true,\n ...groupProps\n}: SplatEnvironmentProps) {\n const metadata = useSplatEnvironment({\n environment,\n scenario,\n renderer,\n src,\n format,\n collisionProxy: collisionProxyMetadata,\n });\n const existingUserData =\n typeof groupProps.userData === 'object' && groupProps.userData !== null\n ? groupProps.userData\n : {};\n\n return (\n <group\n {...groupProps}\n userData={{\n ...existingUserData,\n ...metadata.userData,\n }}\n >\n {children}\n {children || !showPlaceholder ? null : <SplatPlaceholder />}\n {collisionProxy}\n </group>\n );\n}\n\nexport function useSplatEnvironment({\n environment,\n scenario,\n renderer,\n src,\n format,\n collisionProxy,\n}: SplatEnvironmentMetadataInput): SplatEnvironmentMetadata {\n const scenarioEnvironment = useMemo(\n () =>\n environment ??\n (scenario\n ? createPairedSplatEnvironment(scenario, { renderer })\n : undefined),\n [environment, renderer, scenario]\n );\n const resolvedSrc = src ?? scenarioEnvironment?.splat.src ?? scenario?.splat?.src;\n const resolvedFormat =\n format ??\n scenarioEnvironment?.splat.format ??\n scenario?.splat?.format ??\n 'spz';\n const resolvedCollisionProxy =\n collisionProxy ??\n scenarioEnvironment?.collisionProxy ??\n scenario?.splat?.collisionProxy ??\n undefined;\n\n return useMemo(\n () => ({\n src: resolvedSrc,\n format: resolvedFormat,\n collisionProxy: resolvedCollisionProxy,\n userData: createSplatEnvironmentUserData({\n environment: scenarioEnvironment,\n src: resolvedSrc,\n format: resolvedFormat,\n collisionProxy: resolvedCollisionProxy,\n }),\n }),\n [scenarioEnvironment, resolvedSrc, resolvedFormat, resolvedCollisionProxy]\n );\n}\n\n/**\n * Convert a generic visual scenario splat block into a paired visual/physics\n * environment config. Returns undefined until both the splat asset and MJCF\n * collision proxy are present.\n */\nexport function createPairedSplatEnvironment(\n scenario: Pick<VisualScenarioConfig, 'id' | 'label' | 'environment' | 'splat'>,\n options: {\n id?: string;\n label?: string;\n description?: string;\n renderer?: SplatRendererKind;\n } = {}\n): PairedSplatEnvironmentConfig | undefined {\n const splat = scenario.splat;\n const collisionProxy = splat?.collisionProxy;\n\n if (!splat?.enabled || !splat.src || !collisionProxy?.xmlPath) {\n return undefined;\n }\n\n return {\n id: options.id ?? scenario.id ?? 'splat-environment',\n label: options.label ?? scenario.label ?? 'Gaussian splat environment',\n description:\n options.description ??\n (scenario.environment\n ? `Visual ${scenario.environment} splat paired with MJCF collision proxy.`\n : undefined),\n splat: {\n src: splat.src,\n format: splat.format ?? 'spz',\n renderer: options.renderer,\n },\n collisionProxy: {\n ...collisionProxy,\n xmlPath: collisionProxy.xmlPath,\n },\n };\n}\n\nfunction isPairedSplatEnvironment(input: SplatSceneInput): input is PairedSplatEnvironmentConfig {\n return !!input && 'collisionProxy' in input && 'splat' in input;\n}\n\nfunction sceneRelativePath(sceneConfig: SceneConfig, path: string): string {\n const src = sceneConfig.src;\n if (!src) return path;\n\n const base = src.endsWith('/') ? src : src + '/';\n if (path.startsWith(base)) return path.slice(base.length);\n return path;\n}\n\nfunction uniquePaths(paths: readonly string[]): string[] {\n const seen = new Set<string>();\n const result: string[] = [];\n for (const path of paths) {\n if (seen.has(path)) continue;\n seen.add(path);\n result.push(path);\n }\n return result;\n}\n\n/**\n * Compose a MuJoCo scene config with a paired splat collision proxy.\n *\n * This keeps the common hybrid setup declarative:\n * robot XML remains `sceneFile`, the `.spz` remains a visual-only layer, and\n * the paired MJCF collision proxy is added to `environmentFiles`.\n */\nexport function withSplatEnvironment(\n sceneConfig: SceneConfig,\n input: SplatSceneInput,\n options: { renderer?: SplatRendererKind } = {}\n): SceneConfig {\n const environment = isPairedSplatEnvironment(input)\n ? input\n : input\n ? createPairedSplatEnvironment(input, options)\n : undefined;\n const xmlPath = environment?.collisionProxy.xmlPath;\n if (!xmlPath) return sceneConfig;\n\n return {\n ...sceneConfig,\n environmentFiles: uniquePaths([\n ...(sceneConfig.environmentFiles ?? []),\n sceneRelativePath(sceneConfig, xmlPath),\n ]),\n };\n}\n\nexport function createSplatEnvironmentUserData({\n environment,\n src,\n format = 'spz',\n collisionProxy,\n}: {\n environment?: PairedSplatEnvironmentConfig;\n src?: string;\n format?: SplatFormat;\n collisionProxy?: SplatCollisionProxyConfig;\n}) {\n return {\n role: 'splat-environment',\n environmentId: environment?.id,\n environmentLabel: environment?.label,\n splatSrc: src,\n splatFormat: format,\n splatRenderer: environment?.splat.renderer,\n collisionProxyStatus: collisionProxy?.status ?? 'missing',\n collisionProxyXmlPath: collisionProxy?.xmlPath,\n collisionProxyPrimitives: collisionProxy?.primitives ?? [],\n };\n}\n\nexport function createSparkSplatViewerUrl({\n viewerUrl,\n splatSrc,\n}: {\n viewerUrl: string;\n splatSrc: string;\n}) {\n const url = new URL(viewerUrl, 'http://mujoco-react.local');\n url.searchParams.set('splat', splatSrc);\n return viewerUrl.startsWith('http') ? url.toString() : `${url.pathname}${url.search}`;\n}\n\nfunction SplatPlaceholder() {\n return (\n <group>\n <mesh position={[0, 0, 1.2]}>\n <boxGeometry args={[2.4, 2.4, 2.4]} />\n <meshBasicMaterial\n color=\"#8b8b8b\"\n transparent\n opacity={0.06}\n wireframe\n side={THREE.DoubleSide}\n />\n </mesh>\n </group>\n );\n}\n\nfunction createScenarioFog(\n scenario: VisualScenarioConfig,\n background: THREE.ColorRepresentation | undefined,\n fogNear: number | undefined,\n fogFar: number | undefined\n) {\n if (scenario.lighting === 'low-light') {\n return new THREE.Fog(\n background ?? getScenarioBackground(scenario.lighting),\n fogNear ?? 2.5,\n fogFar ?? 9\n );\n }\n\n if (scenario.lighting === 'warehouse') {\n return new THREE.Fog(\n background ?? getScenarioBackground(scenario.lighting),\n fogNear ?? 5,\n fogFar ?? 16\n );\n }\n\n return null;\n}\n\nfunction applyScenarioMaterials(\n scene: THREE.Scene,\n scenario: VisualScenarioConfig,\n snapshots: Map<\n THREE.Material,\n {\n color?: THREE.Color;\n roughness?: number;\n metalness?: number;\n }\n >,\n materialFilter: VisualScenarioEffectsProps['materialFilter']\n) {\n const materials = scenario.materials;\n if (!materials) return;\n\n scene.traverse((object) => {\n if (!(object instanceof THREE.Mesh)) {\n return;\n }\n\n for (const material of normalizeMaterials(object.material)) {\n const mutable = getMutableScenarioMaterial(material);\n if (!mutable) continue;\n if (materialFilter && !materialFilter(object, material)) continue;\n\n if (!snapshots.has(material)) {\n snapshots.set(material, {\n color: mutable.color.clone(),\n roughness: mutable.roughness,\n metalness: mutable.metalness,\n });\n }\n\n applyScenarioMaterial(mutable, object, scenario, materials);\n }\n });\n}\n\nfunction applyScenarioMaterial(\n material: THREE.MeshStandardMaterial | THREE.MeshPhysicalMaterial,\n object: THREE.Object3D,\n scenario: VisualScenarioConfig,\n materials: ScenarioMaterialConfig\n) {\n const seed = scenario.seed ?? 0;\n const objectKey = `${scenario.id ?? 'scenario'}:${object.name}:${material.name}:${seed}`;\n const variation = hashToUnitInterval(objectKey);\n\n if (materials.randomizeObjectColors) {\n material.color.setHSL(variation, 0.38, 0.42);\n }\n\n if (materials.randomizeTableMaterial) {\n material.roughness = clamp01(\n materials.roughness ?? 0.35 + variation * 0.45\n );\n material.metalness = clamp01(\n materials.metalness ?? variation * 0.12\n );\n }\n\n material.needsUpdate = true;\n}\n\nfunction normalizeMaterials(\n material: THREE.Material | THREE.Material[]\n): THREE.Material[] {\n return Array.isArray(material) ? material : [material];\n}\n\nfunction getMutableScenarioMaterial(\n material: THREE.Material\n): THREE.MeshStandardMaterial | THREE.MeshPhysicalMaterial | null {\n if (\n material instanceof THREE.MeshStandardMaterial ||\n material instanceof THREE.MeshPhysicalMaterial\n ) {\n return material;\n }\n\n return null;\n}\n\nfunction hashToUnitInterval(value: string) {\n let hash = 2166136261;\n for (let index = 0; index < value.length; index += 1) {\n hash ^= value.charCodeAt(index);\n hash = Math.imul(hash, 16777619);\n }\n return (hash >>> 0) / 4294967295;\n}\n\nfunction clamp01(value: number) {\n return Math.max(0, Math.min(1, value));\n}\n\nexport type SplatCollisionProxy = ReactNode | ThreeElements['group'];\n"]}