mujoco-react 9.4.0 → 9.6.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 +22 -6
- package/dist/{chunk-VDSEPZYQ.js → chunk-4JHALVB2.js} +397 -4
- package/dist/chunk-4JHALVB2.js.map +1 -0
- package/dist/index.d.ts +26 -4
- package/dist/index.js +497 -431
- package/dist/index.js.map +1 -1
- package/dist/spark.d.ts +27 -3
- package/dist/spark.js +156 -3
- package/dist/spark.js.map +1 -1
- package/dist/{types-BuJ4boaq.d.ts → types-C1rwH74Y.d.ts} +37 -1
- package/package.json +1 -1
- package/src/components/ContactMarkers.tsx +8 -1
- package/src/components/Debug.tsx +154 -3
- package/src/components/DragInteraction.tsx +2 -0
- package/src/components/IkGizmo.tsx +5 -1
- package/src/core/MujocoSimProvider.tsx +5 -5
- package/src/hooks/useIkController.ts +8 -1
- package/src/hooks/useKeyboardIkTarget.ts +170 -0
- package/src/index.ts +5 -0
- package/src/rendering/cameraFrameCapture.ts +259 -28
- package/src/rendering/cameraFrameSource.ts +10 -2
- package/src/spark.tsx +241 -1
- package/src/types.ts +51 -0
- package/dist/chunk-VDSEPZYQ.js.map +0 -1
package/dist/spark.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import * as _sparkjsdev_spark from '@sparkjsdev/spark';
|
|
3
|
-
import { n as SplatEnvironmentProps, q as PairedSplatEnvironmentConfig, S as SceneConfig, t as SplatEnvironmentReadiness, o as VisualScenarioConfig } from './types-
|
|
3
|
+
import { n as SplatEnvironmentProps, q as PairedSplatEnvironmentConfig, S as SceneConfig, t as SplatEnvironmentReadiness, o as VisualScenarioConfig } from './types-C1rwH74Y.js';
|
|
4
4
|
import 'react';
|
|
5
5
|
import '@react-three/fiber';
|
|
6
6
|
import 'three';
|
|
@@ -8,6 +8,26 @@ import 'three';
|
|
|
8
8
|
type SparkModule = typeof _sparkjsdev_spark;
|
|
9
9
|
type SparkSplatMeshInstance = InstanceType<SparkModule['SplatMesh']>;
|
|
10
10
|
type SparkSplatStatus = 'idle' | 'loading' | 'ready' | 'error';
|
|
11
|
+
interface SparkSplatRenderTuning {
|
|
12
|
+
/** Scale Spark's LoD splat budget for the live viewport. */
|
|
13
|
+
lodSplatScale?: number;
|
|
14
|
+
/** Minimum rendered LoD splat size. Higher values trade detail for speed. */
|
|
15
|
+
lodRenderScale?: number;
|
|
16
|
+
/** Minimum delay between Spark sort passes for the live viewport. */
|
|
17
|
+
minSortIntervalMs?: number;
|
|
18
|
+
}
|
|
19
|
+
interface SparkSplatCaptureTuning extends SparkSplatRenderTuning {
|
|
20
|
+
/** Maximum animation frames to wait for first-capture Spark warm-up. Default: 4. */
|
|
21
|
+
maxWarmupFrames?: number;
|
|
22
|
+
/** Number of pixels sampled when deciding whether a capture is blank. Default: 512. */
|
|
23
|
+
blankSampleCount?: number;
|
|
24
|
+
/** Alpha threshold used by the blank-capture detector. Default: 8. */
|
|
25
|
+
blankAlphaThreshold?: number;
|
|
26
|
+
/** Minimum visible sampled-pixel ratio before retrying capture. Default: 0.02. */
|
|
27
|
+
blankVisibleRatio?: number;
|
|
28
|
+
/** Minimum average sampled RGB sum before retrying capture. Default: 3. */
|
|
29
|
+
blankAverageColor?: number;
|
|
30
|
+
}
|
|
11
31
|
interface SparkSplatLifecycle {
|
|
12
32
|
status: SparkSplatStatus;
|
|
13
33
|
error: Error | null;
|
|
@@ -28,6 +48,10 @@ interface SparkSplatEnvironmentState {
|
|
|
28
48
|
interface SparkSplatEnvironmentProps extends SplatEnvironmentProps {
|
|
29
49
|
/** Enable Spark LoD handling for large splat assets. Default: true. */
|
|
30
50
|
lod?: boolean | 'quality';
|
|
51
|
+
/** Tune Spark's live viewport renderer. Defaults favor interactive FPS. */
|
|
52
|
+
renderTuning?: SparkSplatRenderTuning;
|
|
53
|
+
/** Tune Spark camera-frame capture. Defaults favor sharper snapshots. */
|
|
54
|
+
captureTuning?: SparkSplatCaptureTuning;
|
|
31
55
|
/**
|
|
32
56
|
* Hide meshes whose names include floor, ground, or plane while the splat is
|
|
33
57
|
* active. This mirrors the common hybrid-rendering setup where MJCF keeps
|
|
@@ -70,6 +94,6 @@ declare function useSparkSplatLifecycle({ enabled, initialStatus, onError, onSta
|
|
|
70
94
|
* Import from `mujoco-react/spark` and install `@sparkjsdev/spark` in the app
|
|
71
95
|
* that uses it. The core `mujoco-react` entrypoint does not depend on Spark.
|
|
72
96
|
*/
|
|
73
|
-
declare function SparkSplatEnvironment({ environment, scenario, renderer, src, format, collisionProxy, collisionProxyMetadata, showPlaceholder, children, lod, hideGroundMeshes, onStatusChange, onLoad, onError, ...groupProps }: SparkSplatEnvironmentProps): react_jsx_runtime.JSX.Element;
|
|
97
|
+
declare function SparkSplatEnvironment({ environment, scenario, renderer, src, format, collisionProxy, collisionProxyMetadata, showPlaceholder, children, lod, renderTuning, captureTuning, hideGroundMeshes, onStatusChange, onLoad, onError, ...groupProps }: SparkSplatEnvironmentProps): react_jsx_runtime.JSX.Element;
|
|
74
98
|
|
|
75
|
-
export { SparkSplatEnvironment, type SparkSplatEnvironmentProps, type SparkSplatEnvironmentState, type SparkSplatLifecycle, type SparkSplatStatus, useSparkSplatEnvironment, useSparkSplatLifecycle };
|
|
99
|
+
export { type SparkSplatCaptureTuning, SparkSplatEnvironment, type SparkSplatEnvironmentProps, type SparkSplatEnvironmentState, type SparkSplatLifecycle, type SparkSplatRenderTuning, type SparkSplatStatus, useSparkSplatEnvironment, useSparkSplatLifecycle };
|
package/dist/spark.js
CHANGED
|
@@ -1,10 +1,68 @@
|
|
|
1
|
-
import { useSplatSceneConfig, useSplatEnvironment, SplatEnvironment } from './chunk-
|
|
1
|
+
import { useSplatSceneConfig, useSplatEnvironment, SplatEnvironment, CAMERA_FRAME_CAPTURE_RENDER_USER_DATA_KEY } from './chunk-4JHALVB2.js';
|
|
2
2
|
import { useThree } from '@react-three/fiber';
|
|
3
3
|
import { useMemo, useState, useEffect, useCallback, useRef } from 'react';
|
|
4
4
|
import * as THREE from 'three';
|
|
5
5
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
6
6
|
|
|
7
7
|
var sparkDisposeRejectionHandlerRegistered = false;
|
|
8
|
+
function getDefaultLiveRenderTuning(lod) {
|
|
9
|
+
return {
|
|
10
|
+
lodSplatScale: lod === false ? void 0 : lod === "quality" ? 1 : 0.6,
|
|
11
|
+
lodRenderScale: lod === false || lod === "quality" ? void 0 : 1.2,
|
|
12
|
+
minSortIntervalMs: lod === false ? 0 : lod === "quality" ? 32 : 80
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function getDefaultCaptureTuning(lod) {
|
|
16
|
+
return {
|
|
17
|
+
lodSplatScale: lod === false ? void 0 : lod === "quality" ? 1.25 : 1.15,
|
|
18
|
+
lodRenderScale: lod === false ? void 0 : 0.6,
|
|
19
|
+
minSortIntervalMs: 0,
|
|
20
|
+
maxWarmupFrames: 4,
|
|
21
|
+
blankSampleCount: 512,
|
|
22
|
+
blankAlphaThreshold: 8,
|
|
23
|
+
blankVisibleRatio: 0.02,
|
|
24
|
+
blankAverageColor: 3
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function resolveRenderTuning(defaults, tuning) {
|
|
28
|
+
return {
|
|
29
|
+
lodSplatScale: tuning?.lodSplatScale ?? defaults.lodSplatScale,
|
|
30
|
+
lodRenderScale: tuning?.lodRenderScale ?? defaults.lodRenderScale,
|
|
31
|
+
minSortIntervalMs: tuning?.minSortIntervalMs ?? defaults.minSortIntervalMs
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function resolveCaptureTuning(lod, tuning) {
|
|
35
|
+
const defaults = getDefaultCaptureTuning(lod);
|
|
36
|
+
return {
|
|
37
|
+
...resolveRenderTuning(defaults, tuning),
|
|
38
|
+
maxWarmupFrames: tuning?.maxWarmupFrames ?? defaults.maxWarmupFrames,
|
|
39
|
+
blankSampleCount: tuning?.blankSampleCount ?? defaults.blankSampleCount,
|
|
40
|
+
blankAlphaThreshold: tuning?.blankAlphaThreshold ?? defaults.blankAlphaThreshold,
|
|
41
|
+
blankVisibleRatio: tuning?.blankVisibleRatio ?? defaults.blankVisibleRatio,
|
|
42
|
+
blankAverageColor: tuning?.blankAverageColor ?? defaults.blankAverageColor
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function waitForNextAnimationFrame() {
|
|
46
|
+
return new Promise((resolve) => {
|
|
47
|
+
requestAnimationFrame(() => resolve());
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
function isMostlyBlankCapture(pixels, tuning) {
|
|
51
|
+
const sampleCount = Math.max(1, Math.floor(tuning.blankSampleCount));
|
|
52
|
+
const stride = Math.max(4, Math.floor(pixels.length / sampleCount / 4) * 4);
|
|
53
|
+
let sampled = 0;
|
|
54
|
+
let visible = 0;
|
|
55
|
+
let colorTotal = 0;
|
|
56
|
+
for (let i = 0; i < pixels.length; i += stride) {
|
|
57
|
+
const alpha = pixels[i + 3];
|
|
58
|
+
const color = pixels[i] + pixels[i + 1] + pixels[i + 2];
|
|
59
|
+
sampled += 1;
|
|
60
|
+
if (alpha > tuning.blankAlphaThreshold) visible += 1;
|
|
61
|
+
colorTotal += color;
|
|
62
|
+
}
|
|
63
|
+
if (sampled === 0) return true;
|
|
64
|
+
return visible / sampled < tuning.blankVisibleRatio || colorTotal / sampled < tuning.blankAverageColor;
|
|
65
|
+
}
|
|
8
66
|
function useSparkSplatEnvironment({
|
|
9
67
|
sceneConfig,
|
|
10
68
|
scenario,
|
|
@@ -114,6 +172,8 @@ function SparkSplatEnvironment({
|
|
|
114
172
|
showPlaceholder,
|
|
115
173
|
children,
|
|
116
174
|
lod = true,
|
|
175
|
+
renderTuning,
|
|
176
|
+
captureTuning,
|
|
117
177
|
hideGroundMeshes = false,
|
|
118
178
|
onStatusChange,
|
|
119
179
|
onLoad,
|
|
@@ -122,6 +182,7 @@ function SparkSplatEnvironment({
|
|
|
122
182
|
}) {
|
|
123
183
|
const groupRef = useRef(null);
|
|
124
184
|
const sparkRef = useRef(null);
|
|
185
|
+
const captureSparkRef = useRef(null);
|
|
125
186
|
const meshRef = useRef(null);
|
|
126
187
|
const hiddenMeshesRef = useRef([]);
|
|
127
188
|
const onStatusChangeRef = useRef(onStatusChange);
|
|
@@ -137,6 +198,29 @@ function SparkSplatEnvironment({
|
|
|
137
198
|
format,
|
|
138
199
|
collisionProxy: collisionProxyMetadata
|
|
139
200
|
});
|
|
201
|
+
const resolvedRenderTuning = useMemo(
|
|
202
|
+
() => resolveRenderTuning(getDefaultLiveRenderTuning(lod), renderTuning),
|
|
203
|
+
[
|
|
204
|
+
lod,
|
|
205
|
+
renderTuning?.lodRenderScale,
|
|
206
|
+
renderTuning?.lodSplatScale,
|
|
207
|
+
renderTuning?.minSortIntervalMs
|
|
208
|
+
]
|
|
209
|
+
);
|
|
210
|
+
const resolvedCaptureTuning = useMemo(
|
|
211
|
+
() => resolveCaptureTuning(lod, captureTuning),
|
|
212
|
+
[
|
|
213
|
+
captureTuning?.blankAlphaThreshold,
|
|
214
|
+
captureTuning?.blankAverageColor,
|
|
215
|
+
captureTuning?.blankSampleCount,
|
|
216
|
+
captureTuning?.blankVisibleRatio,
|
|
217
|
+
captureTuning?.lodRenderScale,
|
|
218
|
+
captureTuning?.lodSplatScale,
|
|
219
|
+
captureTuning?.maxWarmupFrames,
|
|
220
|
+
captureTuning?.minSortIntervalMs,
|
|
221
|
+
lod
|
|
222
|
+
]
|
|
223
|
+
);
|
|
140
224
|
useEffect(() => {
|
|
141
225
|
onStatusChangeRef.current = onStatusChange;
|
|
142
226
|
}, [onStatusChange]);
|
|
@@ -159,6 +243,10 @@ function SparkSplatEnvironment({
|
|
|
159
243
|
}
|
|
160
244
|
hiddenMeshesRef.current = [];
|
|
161
245
|
}
|
|
246
|
+
function disposeCaptureSpark() {
|
|
247
|
+
safelyDisposeSparkResource(captureSparkRef.current?.renderer);
|
|
248
|
+
captureSparkRef.current = null;
|
|
249
|
+
}
|
|
162
250
|
async function loadSplat() {
|
|
163
251
|
if (!metadata.src) {
|
|
164
252
|
setLifecycleStatus("idle");
|
|
@@ -178,8 +266,60 @@ function SparkSplatEnvironment({
|
|
|
178
266
|
if (disposed || !groupRef.current) return;
|
|
179
267
|
const spark = new sparkModule.SparkRenderer({
|
|
180
268
|
renderer: gl,
|
|
181
|
-
onDirty: invalidate
|
|
269
|
+
onDirty: invalidate,
|
|
270
|
+
...resolvedRenderTuning
|
|
182
271
|
});
|
|
272
|
+
spark.userData[CAMERA_FRAME_CAPTURE_RENDER_USER_DATA_KEY] = async ({
|
|
273
|
+
scene,
|
|
274
|
+
camera,
|
|
275
|
+
width,
|
|
276
|
+
height
|
|
277
|
+
}) => {
|
|
278
|
+
let captureSpark = captureSparkRef.current;
|
|
279
|
+
if (!captureSpark || captureSpark.width !== width || captureSpark.height !== height) {
|
|
280
|
+
disposeCaptureSpark();
|
|
281
|
+
captureSpark = {
|
|
282
|
+
renderer: new sparkModule.SparkRenderer({
|
|
283
|
+
renderer: gl,
|
|
284
|
+
autoUpdate: false,
|
|
285
|
+
lodSplatScale: resolvedCaptureTuning.lodSplatScale,
|
|
286
|
+
lodRenderScale: resolvedCaptureTuning.lodRenderScale,
|
|
287
|
+
minSortIntervalMs: resolvedCaptureTuning.minSortIntervalMs,
|
|
288
|
+
target: {
|
|
289
|
+
width,
|
|
290
|
+
height
|
|
291
|
+
}
|
|
292
|
+
}),
|
|
293
|
+
width,
|
|
294
|
+
height
|
|
295
|
+
};
|
|
296
|
+
captureSparkRef.current = captureSpark;
|
|
297
|
+
}
|
|
298
|
+
const captureRenderer = captureSpark.renderer;
|
|
299
|
+
const maxWarmupFrames = Math.max(
|
|
300
|
+
1,
|
|
301
|
+
Math.floor(resolvedCaptureTuning.maxWarmupFrames)
|
|
302
|
+
);
|
|
303
|
+
let pixels;
|
|
304
|
+
for (let attempt = 0; attempt < maxWarmupFrames; attempt += 1) {
|
|
305
|
+
captureRenderer.renderSize.set(width, height);
|
|
306
|
+
await captureRenderer.update({ scene, camera });
|
|
307
|
+
pixels = await captureRenderer.renderReadTarget({
|
|
308
|
+
scene,
|
|
309
|
+
camera
|
|
310
|
+
});
|
|
311
|
+
if (captureRenderer.activeSplats > 0 && !isMostlyBlankCapture(pixels, resolvedCaptureTuning)) {
|
|
312
|
+
break;
|
|
313
|
+
}
|
|
314
|
+
if (attempt < maxWarmupFrames - 1) {
|
|
315
|
+
await waitForNextAnimationFrame();
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
if (!pixels) {
|
|
319
|
+
throw new Error("Spark camera frame capture did not produce pixels.");
|
|
320
|
+
}
|
|
321
|
+
return { pixels, width, height, flipY: true };
|
|
322
|
+
};
|
|
183
323
|
const mesh = new sparkModule.SplatMesh({
|
|
184
324
|
url: metadata.src,
|
|
185
325
|
lod
|
|
@@ -226,6 +366,7 @@ function SparkSplatEnvironment({
|
|
|
226
366
|
safelyDisposeSparkResource(sparkRef.current);
|
|
227
367
|
sparkRef.current = null;
|
|
228
368
|
}
|
|
369
|
+
disposeCaptureSpark();
|
|
229
370
|
};
|
|
230
371
|
}, [
|
|
231
372
|
gl,
|
|
@@ -233,7 +374,18 @@ function SparkSplatEnvironment({
|
|
|
233
374
|
invalidate,
|
|
234
375
|
lod,
|
|
235
376
|
metadata.format,
|
|
236
|
-
metadata.src
|
|
377
|
+
metadata.src,
|
|
378
|
+
resolvedCaptureTuning.blankAlphaThreshold,
|
|
379
|
+
resolvedCaptureTuning.blankAverageColor,
|
|
380
|
+
resolvedCaptureTuning.blankSampleCount,
|
|
381
|
+
resolvedCaptureTuning.blankVisibleRatio,
|
|
382
|
+
resolvedCaptureTuning.lodRenderScale,
|
|
383
|
+
resolvedCaptureTuning.lodSplatScale,
|
|
384
|
+
resolvedCaptureTuning.maxWarmupFrames,
|
|
385
|
+
resolvedCaptureTuning.minSortIntervalMs,
|
|
386
|
+
resolvedRenderTuning.lodRenderScale,
|
|
387
|
+
resolvedRenderTuning.lodSplatScale,
|
|
388
|
+
resolvedRenderTuning.minSortIntervalMs
|
|
237
389
|
]);
|
|
238
390
|
return /* @__PURE__ */ jsxs(
|
|
239
391
|
SplatEnvironment,
|
|
@@ -255,6 +407,7 @@ function SparkSplatEnvironment({
|
|
|
255
407
|
);
|
|
256
408
|
}
|
|
257
409
|
function safelyDisposeSparkResource(resource) {
|
|
410
|
+
if (!resource) return;
|
|
258
411
|
try {
|
|
259
412
|
silenceSparkWorkerTerminateRejections(resource);
|
|
260
413
|
const result = resource.dispose?.();
|
package/dist/spark.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/spark.tsx"],"names":[],"mappings":"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","file":"spark.js","sourcesContent":["/**\n * @license\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { useThree } from '@react-three/fiber';\nimport {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport * as THREE from 'three';\nimport {\n SplatEnvironment,\n useSplatEnvironment,\n useSplatSceneConfig,\n} from './components/VisualScenario';\nimport type {\n PairedSplatEnvironmentConfig,\n SceneConfig,\n SplatEnvironmentProps,\n SplatEnvironmentReadiness,\n VisualScenarioConfig,\n} from './types';\n\ntype SparkModule = typeof import('@sparkjsdev/spark');\ntype SparkRendererInstance = InstanceType<SparkModule['SparkRenderer']>;\ntype SparkSplatMeshInstance = InstanceType<SparkModule['SplatMesh']>;\ntype SparkDisposable = {\n dispose?: () => unknown;\n};\ntype SparkWorkerMessage = {\n reject?: (error: unknown) => void;\n};\ntype SparkWorkerLike = {\n messages?: Record<string, SparkWorkerMessage>;\n};\ntype SparkResourceWithWorkers = SparkDisposable & {\n worker?: SparkWorkerLike;\n sortWorker?: SparkWorkerLike;\n lodWorker?: SparkWorkerLike;\n};\n\nexport type SparkSplatStatus = 'idle' | 'loading' | 'ready' | 'error';\n\nlet sparkDisposeRejectionHandlerRegistered = false;\n\nexport interface SparkSplatLifecycle {\n status: SparkSplatStatus;\n error: Error | null;\n isLoading: boolean;\n isReady: boolean;\n isError: boolean;\n props: Pick<SparkSplatEnvironmentProps, 'onStatusChange' | 'onError'>;\n reset: () => void;\n}\n\nexport interface SparkSplatEnvironmentState {\n environment: PairedSplatEnvironmentConfig | undefined;\n sceneConfig: SceneConfig;\n readiness: SplatEnvironmentReadiness;\n lifecycle: SparkSplatLifecycle;\n props: Pick<\n SparkSplatEnvironmentProps,\n 'environment' | 'scenario' | 'src' | 'format' | 'onStatusChange' | 'onError'\n >;\n enabled: boolean;\n}\n\nexport interface SparkSplatEnvironmentProps extends SplatEnvironmentProps {\n /** Enable Spark LoD handling for large splat assets. Default: true. */\n lod?: boolean | 'quality';\n /**\n * Hide meshes whose names include floor, ground, or plane while the splat is\n * active. This mirrors the common hybrid-rendering setup where MJCF keeps\n * collision geometry but the splat owns the visual environment.\n */\n hideGroundMeshes?: boolean;\n onStatusChange?: (status: SparkSplatStatus) => void;\n onLoad?: (mesh: SparkSplatMeshInstance) => void;\n onError?: (error: Error) => void;\n}\n\n/**\n * Resolve a visual scenario's paired splat environment, compose its MJCF\n * collision proxy into the MuJoCo scene config, and expose Spark lifecycle\n * props for `<SparkSplatEnvironment />`.\n */\nexport function useSparkSplatEnvironment({\n sceneConfig,\n scenario,\n environment,\n enabled = true,\n renderer = 'spark',\n onError,\n onStatusChange,\n}: {\n sceneConfig: SceneConfig;\n scenario?: VisualScenarioConfig;\n environment?: PairedSplatEnvironmentConfig;\n enabled?: boolean;\n renderer?: 'spark';\n onError?: (error: Error) => void;\n onStatusChange?: (status: SparkSplatStatus) => void;\n}): SparkSplatEnvironmentState {\n const splatScene = useSplatSceneConfig({\n sceneConfig,\n scenario,\n environment,\n enabled,\n renderer,\n });\n const metadata = useSplatEnvironment({\n scenario,\n environment: splatScene.environment,\n renderer,\n });\n const renderEnabled = enabled && Boolean(metadata.src);\n const readiness = enabled ? metadata.readiness : splatScene.readiness;\n const lifecycle = useSparkSplatLifecycle({\n enabled: renderEnabled,\n onError,\n onStatusChange,\n });\n\n return useMemo(\n () => ({\n environment: splatScene.environment,\n sceneConfig: splatScene.sceneConfig,\n readiness,\n lifecycle,\n props: {\n environment: splatScene.environment,\n scenario: enabled ? scenario : undefined,\n src: enabled ? metadata.src : undefined,\n format: metadata.format,\n ...lifecycle.props,\n },\n enabled: renderEnabled,\n }),\n [enabled, lifecycle, metadata, readiness, renderEnabled, scenario, splatScene]\n );\n}\n\n/**\n * Tracks Spark 3DGS loading state for UI that wraps `SparkSplatEnvironment`.\n *\n * Use the returned `props` with `<SparkSplatEnvironment {...lifecycle.props} />`\n * to avoid repeating status/error state in app code.\n */\nexport function useSparkSplatLifecycle({\n enabled = true,\n initialStatus,\n onError,\n onStatusChange,\n}: {\n enabled?: boolean;\n initialStatus?: SparkSplatStatus;\n onError?: (error: Error) => void;\n onStatusChange?: (status: SparkSplatStatus) => void;\n} = {}): SparkSplatLifecycle {\n const [status, setStatus] = useState<SparkSplatStatus>(\n initialStatus ?? (enabled ? 'loading' : 'idle')\n );\n const [error, setError] = useState<Error | null>(null);\n\n useEffect(() => {\n setStatus(enabled ? initialStatus ?? 'loading' : 'idle');\n setError(null);\n }, [enabled, initialStatus]);\n\n const handleStatusChange = useCallback(\n (nextStatus: SparkSplatStatus) => {\n setStatus(nextStatus);\n if (nextStatus !== 'error') {\n setError(null);\n }\n onStatusChange?.(nextStatus);\n },\n [onStatusChange]\n );\n\n const handleError = useCallback(\n (nextError: Error) => {\n setError(nextError);\n setStatus('error');\n onError?.(nextError);\n },\n [onError]\n );\n\n const reset = useCallback(() => {\n setStatus(enabled ? initialStatus ?? 'loading' : 'idle');\n setError(null);\n }, [enabled, initialStatus]);\n\n return useMemo(\n () => ({\n status,\n error,\n isLoading: status === 'loading',\n isReady: status === 'ready',\n isError: status === 'error',\n props: {\n onStatusChange: handleStatusChange,\n onError: handleError,\n },\n reset,\n }),\n [error, handleError, handleStatusChange, reset, status]\n );\n}\n\n/**\n * Optional SparkJS-backed Gaussian splat renderer for React Three Fiber scenes.\n *\n * Import from `mujoco-react/spark` and install `@sparkjsdev/spark` in the app\n * that uses it. The core `mujoco-react` entrypoint does not depend on Spark.\n */\nexport function SparkSplatEnvironment({\n environment,\n scenario,\n renderer = 'spark',\n src,\n format,\n collisionProxy,\n collisionProxyMetadata,\n showPlaceholder,\n children,\n lod = true,\n hideGroundMeshes = false,\n onStatusChange,\n onLoad,\n onError,\n ...groupProps\n}: SparkSplatEnvironmentProps) {\n const groupRef = useRef<THREE.Group>(null);\n const sparkRef = useRef<SparkRendererInstance | null>(null);\n const meshRef = useRef<SparkSplatMeshInstance | null>(null);\n const hiddenMeshesRef = useRef<THREE.Mesh[]>([]);\n const onStatusChangeRef = useRef(onStatusChange);\n const onLoadRef = useRef(onLoad);\n const onErrorRef = useRef(onError);\n const [status, setStatus] = useState<SparkSplatStatus>('idle');\n const { gl, invalidate } = useThree();\n const metadata = useSplatEnvironment({\n environment,\n scenario,\n renderer,\n src,\n format,\n collisionProxy: collisionProxyMetadata,\n });\n\n useEffect(() => {\n onStatusChangeRef.current = onStatusChange;\n }, [onStatusChange]);\n\n useEffect(() => {\n onLoadRef.current = onLoad;\n }, [onLoad]);\n\n useEffect(() => {\n onErrorRef.current = onError;\n }, [onError]);\n\n useEffect(() => {\n let disposed = false;\n ensureSparkDisposeRejectionHandler();\n\n function setLifecycleStatus(nextStatus: SparkSplatStatus) {\n setStatus(nextStatus);\n onStatusChangeRef.current?.(nextStatus);\n }\n\n function restoreHiddenMeshes() {\n for (const mesh of hiddenMeshesRef.current) {\n mesh.visible = true;\n }\n hiddenMeshesRef.current = [];\n }\n\n async function loadSplat() {\n if (!metadata.src) {\n setLifecycleStatus('idle');\n return;\n }\n\n if (metadata.format !== 'spz') {\n const unsupportedFormatError = new Error(\n `SparkSplatEnvironment only supports .spz assets; received \"${metadata.format}\".`\n );\n setLifecycleStatus('error');\n onErrorRef.current?.(unsupportedFormatError);\n return;\n }\n\n setLifecycleStatus('loading');\n\n try {\n const sparkModule = await import('@sparkjsdev/spark');\n if (disposed || !groupRef.current) return;\n\n const spark = new sparkModule.SparkRenderer({\n renderer: gl,\n onDirty: invalidate,\n });\n const mesh = new sparkModule.SplatMesh({\n url: metadata.src,\n lod,\n });\n mesh.name = 'GaussianSplatMesh';\n\n groupRef.current.add(spark);\n groupRef.current.add(mesh);\n sparkRef.current = spark;\n meshRef.current = mesh;\n\n if (hideGroundMeshes && groupRef.current.parent) {\n groupRef.current.parent.traverse((object) => {\n if (\n !(object instanceof THREE.Mesh) ||\n object === (mesh as unknown as THREE.Object3D)\n ) {\n return;\n }\n const name = object.name.toLowerCase();\n if (\n name.includes('floor') ||\n name.includes('ground') ||\n name.includes('plane')\n ) {\n object.visible = false;\n hiddenMeshesRef.current.push(object);\n }\n });\n }\n\n await mesh.initialized;\n if (disposed) return;\n setLifecycleStatus('ready');\n onLoadRef.current?.(mesh);\n invalidate();\n } catch (error) {\n const normalizedError =\n error instanceof Error ? error : new Error(String(error));\n setLifecycleStatus('error');\n onErrorRef.current?.(normalizedError);\n }\n }\n\n void loadSplat();\n\n return () => {\n disposed = true;\n restoreHiddenMeshes();\n\n if (meshRef.current) {\n groupRef.current?.remove(meshRef.current);\n safelyDisposeSparkResource(meshRef.current);\n meshRef.current = null;\n }\n\n if (sparkRef.current) {\n groupRef.current?.remove(sparkRef.current);\n safelyDisposeSparkResource(sparkRef.current);\n sparkRef.current = null;\n }\n };\n }, [\n gl,\n hideGroundMeshes,\n invalidate,\n lod,\n metadata.format,\n metadata.src,\n ]);\n\n return (\n <SplatEnvironment\n {...groupProps}\n environment={environment}\n scenario={scenario}\n renderer={renderer}\n src={metadata.src}\n format={metadata.format}\n collisionProxyMetadata={metadata.collisionProxy}\n collisionProxy={collisionProxy}\n showPlaceholder={showPlaceholder ?? status !== 'ready'}\n >\n <group ref={groupRef} />\n {children}\n </SplatEnvironment>\n );\n}\n\nfunction safelyDisposeSparkResource(resource: SparkDisposable) {\n try {\n silenceSparkWorkerTerminateRejections(resource);\n const result = resource.dispose?.();\n if (isPromiseLike(result)) {\n void Promise.resolve(result).catch(handleSparkDisposeError);\n }\n } catch (error) {\n handleSparkDisposeError(error);\n }\n}\n\nfunction isPromiseLike(value: unknown): value is PromiseLike<unknown> {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'then' in value &&\n typeof (value as { then?: unknown }).then === 'function'\n );\n}\n\nfunction silenceSparkWorkerTerminateRejections(resource: SparkDisposable) {\n const workers = getSparkWorkers(resource);\n for (const worker of workers) {\n if (!worker.messages) continue;\n\n for (const message of Object.values(worker.messages)) {\n const reject = message.reject;\n if (!reject) continue;\n\n message.reject = (error: unknown) => {\n if (!isSparkWorkerTerminateError(error)) {\n reject(error);\n }\n };\n }\n }\n}\n\nfunction getSparkWorkers(resource: SparkDisposable): SparkWorkerLike[] {\n const sparkResource = resource as SparkResourceWithWorkers;\n return [\n sparkResource.worker,\n sparkResource.sortWorker,\n sparkResource.lodWorker,\n ].filter((worker): worker is SparkWorkerLike => Boolean(worker));\n}\n\nfunction handleSparkDisposeError(error: unknown) {\n if (\n error instanceof Error &&\n error.message.toLowerCase().includes('worker terminate')\n ) {\n return;\n }\n\n console.warn('[mujoco-react] Spark resource disposal failed.', error);\n}\n\nfunction ensureSparkDisposeRejectionHandler() {\n if (\n sparkDisposeRejectionHandlerRegistered ||\n typeof window === 'undefined' ||\n typeof window.addEventListener !== 'function'\n ) {\n return;\n }\n\n sparkDisposeRejectionHandlerRegistered = true;\n window.addEventListener('unhandledrejection', (event) => {\n if (isSparkWorkerTerminateError(event.reason)) {\n event.preventDefault();\n }\n });\n}\n\nfunction isSparkWorkerTerminateError(reason: unknown) {\n return (\n reason instanceof Error &&\n reason.message.toLowerCase().includes('worker terminate')\n );\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/spark.tsx"],"names":[],"mappings":"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","file":"spark.js","sourcesContent":["/**\n * @license\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { useThree } from '@react-three/fiber';\nimport {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport * as THREE from 'three';\nimport {\n SplatEnvironment,\n useSplatEnvironment,\n useSplatSceneConfig,\n} from './components/VisualScenario';\nimport {\n CAMERA_FRAME_CAPTURE_RENDER_USER_DATA_KEY,\n} from './rendering/cameraFrameCapture';\nimport type {\n CameraFrameCaptureRenderInput,\n CameraFrameCaptureRenderResult,\n} from './rendering/cameraFrameCapture';\nimport type {\n PairedSplatEnvironmentConfig,\n SceneConfig,\n SplatEnvironmentProps,\n SplatEnvironmentReadiness,\n VisualScenarioConfig,\n} from './types';\n\ntype SparkModule = typeof import('@sparkjsdev/spark');\ntype SparkRendererInstance = InstanceType<SparkModule['SparkRenderer']>;\ntype SparkSplatMeshInstance = InstanceType<SparkModule['SplatMesh']>;\ntype SparkDisposable = {\n dispose?: () => unknown;\n};\ntype SparkCaptureRenderer = {\n renderer: SparkRendererInstance;\n width: number;\n height: number;\n};\ntype SparkWorkerMessage = {\n reject?: (error: unknown) => void;\n};\ntype SparkWorkerLike = {\n messages?: Record<string, SparkWorkerMessage>;\n};\ntype SparkResourceWithWorkers = SparkDisposable & {\n worker?: SparkWorkerLike;\n sortWorker?: SparkWorkerLike;\n lodWorker?: SparkWorkerLike;\n};\n\nexport type SparkSplatStatus = 'idle' | 'loading' | 'ready' | 'error';\n\nlet sparkDisposeRejectionHandlerRegistered = false;\n\nexport interface SparkSplatRenderTuning {\n /** Scale Spark's LoD splat budget for the live viewport. */\n lodSplatScale?: number;\n /** Minimum rendered LoD splat size. Higher values trade detail for speed. */\n lodRenderScale?: number;\n /** Minimum delay between Spark sort passes for the live viewport. */\n minSortIntervalMs?: number;\n}\n\nexport interface SparkSplatCaptureTuning extends SparkSplatRenderTuning {\n /** Maximum animation frames to wait for first-capture Spark warm-up. Default: 4. */\n maxWarmupFrames?: number;\n /** Number of pixels sampled when deciding whether a capture is blank. Default: 512. */\n blankSampleCount?: number;\n /** Alpha threshold used by the blank-capture detector. Default: 8. */\n blankAlphaThreshold?: number;\n /** Minimum visible sampled-pixel ratio before retrying capture. Default: 0.02. */\n blankVisibleRatio?: number;\n /** Minimum average sampled RGB sum before retrying capture. Default: 3. */\n blankAverageColor?: number;\n}\n\ntype ResolvedSparkSplatCaptureTuning = Required<\n Pick<\n SparkSplatCaptureTuning,\n | 'maxWarmupFrames'\n | 'blankSampleCount'\n | 'blankAlphaThreshold'\n | 'blankVisibleRatio'\n | 'blankAverageColor'\n >\n> &\n SparkSplatRenderTuning;\n\nfunction getDefaultLiveRenderTuning(\n lod: SparkSplatEnvironmentProps['lod']\n): SparkSplatRenderTuning {\n return {\n lodSplatScale: lod === false ? undefined : lod === 'quality' ? 1 : 0.6,\n lodRenderScale: lod === false || lod === 'quality' ? undefined : 1.2,\n minSortIntervalMs: lod === false ? 0 : lod === 'quality' ? 32 : 80,\n };\n}\n\nfunction getDefaultCaptureTuning(\n lod: SparkSplatEnvironmentProps['lod']\n): ResolvedSparkSplatCaptureTuning {\n return {\n lodSplatScale: lod === false ? undefined : lod === 'quality' ? 1.25 : 1.15,\n lodRenderScale: lod === false ? undefined : 0.6,\n minSortIntervalMs: 0,\n maxWarmupFrames: 4,\n blankSampleCount: 512,\n blankAlphaThreshold: 8,\n blankVisibleRatio: 0.02,\n blankAverageColor: 3,\n };\n}\n\nfunction resolveRenderTuning(\n defaults: SparkSplatRenderTuning,\n tuning: SparkSplatRenderTuning | undefined\n): SparkSplatRenderTuning {\n return {\n lodSplatScale: tuning?.lodSplatScale ?? defaults.lodSplatScale,\n lodRenderScale: tuning?.lodRenderScale ?? defaults.lodRenderScale,\n minSortIntervalMs: tuning?.minSortIntervalMs ?? defaults.minSortIntervalMs,\n };\n}\n\nfunction resolveCaptureTuning(\n lod: SparkSplatEnvironmentProps['lod'],\n tuning: SparkSplatCaptureTuning | undefined\n): ResolvedSparkSplatCaptureTuning {\n const defaults = getDefaultCaptureTuning(lod);\n return {\n ...resolveRenderTuning(defaults, tuning),\n maxWarmupFrames: tuning?.maxWarmupFrames ?? defaults.maxWarmupFrames,\n blankSampleCount: tuning?.blankSampleCount ?? defaults.blankSampleCount,\n blankAlphaThreshold:\n tuning?.blankAlphaThreshold ?? defaults.blankAlphaThreshold,\n blankVisibleRatio: tuning?.blankVisibleRatio ?? defaults.blankVisibleRatio,\n blankAverageColor: tuning?.blankAverageColor ?? defaults.blankAverageColor,\n };\n}\n\nfunction waitForNextAnimationFrame() {\n return new Promise<void>((resolve) => {\n requestAnimationFrame(() => resolve());\n });\n}\n\nfunction isMostlyBlankCapture(\n pixels: Uint8Array,\n tuning: ResolvedSparkSplatCaptureTuning\n) {\n const sampleCount = Math.max(1, Math.floor(tuning.blankSampleCount));\n const stride = Math.max(4, Math.floor(pixels.length / sampleCount / 4) * 4);\n let sampled = 0;\n let visible = 0;\n let colorTotal = 0;\n\n for (let i = 0; i < pixels.length; i += stride) {\n const alpha = pixels[i + 3];\n const color = pixels[i] + pixels[i + 1] + pixels[i + 2];\n sampled += 1;\n if (alpha > tuning.blankAlphaThreshold) visible += 1;\n colorTotal += color;\n }\n\n if (sampled === 0) return true;\n return (\n visible / sampled < tuning.blankVisibleRatio ||\n colorTotal / sampled < tuning.blankAverageColor\n );\n}\n\nexport interface SparkSplatLifecycle {\n status: SparkSplatStatus;\n error: Error | null;\n isLoading: boolean;\n isReady: boolean;\n isError: boolean;\n props: Pick<SparkSplatEnvironmentProps, 'onStatusChange' | 'onError'>;\n reset: () => void;\n}\n\nexport interface SparkSplatEnvironmentState {\n environment: PairedSplatEnvironmentConfig | undefined;\n sceneConfig: SceneConfig;\n readiness: SplatEnvironmentReadiness;\n lifecycle: SparkSplatLifecycle;\n props: Pick<\n SparkSplatEnvironmentProps,\n 'environment' | 'scenario' | 'src' | 'format' | 'onStatusChange' | 'onError'\n >;\n enabled: boolean;\n}\n\nexport interface SparkSplatEnvironmentProps extends SplatEnvironmentProps {\n /** Enable Spark LoD handling for large splat assets. Default: true. */\n lod?: boolean | 'quality';\n /** Tune Spark's live viewport renderer. Defaults favor interactive FPS. */\n renderTuning?: SparkSplatRenderTuning;\n /** Tune Spark camera-frame capture. Defaults favor sharper snapshots. */\n captureTuning?: SparkSplatCaptureTuning;\n /**\n * Hide meshes whose names include floor, ground, or plane while the splat is\n * active. This mirrors the common hybrid-rendering setup where MJCF keeps\n * collision geometry but the splat owns the visual environment.\n */\n hideGroundMeshes?: boolean;\n onStatusChange?: (status: SparkSplatStatus) => void;\n onLoad?: (mesh: SparkSplatMeshInstance) => void;\n onError?: (error: Error) => void;\n}\n\n/**\n * Resolve a visual scenario's paired splat environment, compose its MJCF\n * collision proxy into the MuJoCo scene config, and expose Spark lifecycle\n * props for `<SparkSplatEnvironment />`.\n */\nexport function useSparkSplatEnvironment({\n sceneConfig,\n scenario,\n environment,\n enabled = true,\n renderer = 'spark',\n onError,\n onStatusChange,\n}: {\n sceneConfig: SceneConfig;\n scenario?: VisualScenarioConfig;\n environment?: PairedSplatEnvironmentConfig;\n enabled?: boolean;\n renderer?: 'spark';\n onError?: (error: Error) => void;\n onStatusChange?: (status: SparkSplatStatus) => void;\n}): SparkSplatEnvironmentState {\n const splatScene = useSplatSceneConfig({\n sceneConfig,\n scenario,\n environment,\n enabled,\n renderer,\n });\n const metadata = useSplatEnvironment({\n scenario,\n environment: splatScene.environment,\n renderer,\n });\n const renderEnabled = enabled && Boolean(metadata.src);\n const readiness = enabled ? metadata.readiness : splatScene.readiness;\n const lifecycle = useSparkSplatLifecycle({\n enabled: renderEnabled,\n onError,\n onStatusChange,\n });\n\n return useMemo(\n () => ({\n environment: splatScene.environment,\n sceneConfig: splatScene.sceneConfig,\n readiness,\n lifecycle,\n props: {\n environment: splatScene.environment,\n scenario: enabled ? scenario : undefined,\n src: enabled ? metadata.src : undefined,\n format: metadata.format,\n ...lifecycle.props,\n },\n enabled: renderEnabled,\n }),\n [enabled, lifecycle, metadata, readiness, renderEnabled, scenario, splatScene]\n );\n}\n\n/**\n * Tracks Spark 3DGS loading state for UI that wraps `SparkSplatEnvironment`.\n *\n * Use the returned `props` with `<SparkSplatEnvironment {...lifecycle.props} />`\n * to avoid repeating status/error state in app code.\n */\nexport function useSparkSplatLifecycle({\n enabled = true,\n initialStatus,\n onError,\n onStatusChange,\n}: {\n enabled?: boolean;\n initialStatus?: SparkSplatStatus;\n onError?: (error: Error) => void;\n onStatusChange?: (status: SparkSplatStatus) => void;\n} = {}): SparkSplatLifecycle {\n const [status, setStatus] = useState<SparkSplatStatus>(\n initialStatus ?? (enabled ? 'loading' : 'idle')\n );\n const [error, setError] = useState<Error | null>(null);\n\n useEffect(() => {\n setStatus(enabled ? initialStatus ?? 'loading' : 'idle');\n setError(null);\n }, [enabled, initialStatus]);\n\n const handleStatusChange = useCallback(\n (nextStatus: SparkSplatStatus) => {\n setStatus(nextStatus);\n if (nextStatus !== 'error') {\n setError(null);\n }\n onStatusChange?.(nextStatus);\n },\n [onStatusChange]\n );\n\n const handleError = useCallback(\n (nextError: Error) => {\n setError(nextError);\n setStatus('error');\n onError?.(nextError);\n },\n [onError]\n );\n\n const reset = useCallback(() => {\n setStatus(enabled ? initialStatus ?? 'loading' : 'idle');\n setError(null);\n }, [enabled, initialStatus]);\n\n return useMemo(\n () => ({\n status,\n error,\n isLoading: status === 'loading',\n isReady: status === 'ready',\n isError: status === 'error',\n props: {\n onStatusChange: handleStatusChange,\n onError: handleError,\n },\n reset,\n }),\n [error, handleError, handleStatusChange, reset, status]\n );\n}\n\n/**\n * Optional SparkJS-backed Gaussian splat renderer for React Three Fiber scenes.\n *\n * Import from `mujoco-react/spark` and install `@sparkjsdev/spark` in the app\n * that uses it. The core `mujoco-react` entrypoint does not depend on Spark.\n */\nexport function SparkSplatEnvironment({\n environment,\n scenario,\n renderer = 'spark',\n src,\n format,\n collisionProxy,\n collisionProxyMetadata,\n showPlaceholder,\n children,\n lod = true,\n renderTuning,\n captureTuning,\n hideGroundMeshes = false,\n onStatusChange,\n onLoad,\n onError,\n ...groupProps\n}: SparkSplatEnvironmentProps) {\n const groupRef = useRef<THREE.Group>(null);\n const sparkRef = useRef<SparkRendererInstance | null>(null);\n const captureSparkRef = useRef<SparkCaptureRenderer | null>(null);\n const meshRef = useRef<SparkSplatMeshInstance | null>(null);\n const hiddenMeshesRef = useRef<THREE.Mesh[]>([]);\n const onStatusChangeRef = useRef(onStatusChange);\n const onLoadRef = useRef(onLoad);\n const onErrorRef = useRef(onError);\n const [status, setStatus] = useState<SparkSplatStatus>('idle');\n const { gl, invalidate } = useThree();\n const metadata = useSplatEnvironment({\n environment,\n scenario,\n renderer,\n src,\n format,\n collisionProxy: collisionProxyMetadata,\n });\n const resolvedRenderTuning = useMemo(\n () => resolveRenderTuning(getDefaultLiveRenderTuning(lod), renderTuning),\n [\n lod,\n renderTuning?.lodRenderScale,\n renderTuning?.lodSplatScale,\n renderTuning?.minSortIntervalMs,\n ]\n );\n const resolvedCaptureTuning = useMemo(\n () => resolveCaptureTuning(lod, captureTuning),\n [\n captureTuning?.blankAlphaThreshold,\n captureTuning?.blankAverageColor,\n captureTuning?.blankSampleCount,\n captureTuning?.blankVisibleRatio,\n captureTuning?.lodRenderScale,\n captureTuning?.lodSplatScale,\n captureTuning?.maxWarmupFrames,\n captureTuning?.minSortIntervalMs,\n lod,\n ]\n );\n\n useEffect(() => {\n onStatusChangeRef.current = onStatusChange;\n }, [onStatusChange]);\n\n useEffect(() => {\n onLoadRef.current = onLoad;\n }, [onLoad]);\n\n useEffect(() => {\n onErrorRef.current = onError;\n }, [onError]);\n\n useEffect(() => {\n let disposed = false;\n ensureSparkDisposeRejectionHandler();\n\n function setLifecycleStatus(nextStatus: SparkSplatStatus) {\n setStatus(nextStatus);\n onStatusChangeRef.current?.(nextStatus);\n }\n\n function restoreHiddenMeshes() {\n for (const mesh of hiddenMeshesRef.current) {\n mesh.visible = true;\n }\n hiddenMeshesRef.current = [];\n }\n\n function disposeCaptureSpark() {\n safelyDisposeSparkResource(captureSparkRef.current?.renderer);\n captureSparkRef.current = null;\n }\n\n async function loadSplat() {\n if (!metadata.src) {\n setLifecycleStatus('idle');\n return;\n }\n\n if (metadata.format !== 'spz') {\n const unsupportedFormatError = new Error(\n `SparkSplatEnvironment only supports .spz assets; received \"${metadata.format}\".`\n );\n setLifecycleStatus('error');\n onErrorRef.current?.(unsupportedFormatError);\n return;\n }\n\n setLifecycleStatus('loading');\n\n try {\n const sparkModule = await import('@sparkjsdev/spark');\n if (disposed || !groupRef.current) return;\n\n const spark = new sparkModule.SparkRenderer({\n renderer: gl,\n onDirty: invalidate,\n ...resolvedRenderTuning,\n });\n spark.userData[CAMERA_FRAME_CAPTURE_RENDER_USER_DATA_KEY] = async ({\n scene,\n camera,\n width,\n height,\n }: CameraFrameCaptureRenderInput): Promise<CameraFrameCaptureRenderResult> => {\n let captureSpark = captureSparkRef.current;\n if (\n !captureSpark ||\n captureSpark.width !== width ||\n captureSpark.height !== height\n ) {\n disposeCaptureSpark();\n captureSpark = {\n renderer: new sparkModule.SparkRenderer({\n renderer: gl,\n autoUpdate: false,\n lodSplatScale: resolvedCaptureTuning.lodSplatScale,\n lodRenderScale: resolvedCaptureTuning.lodRenderScale,\n minSortIntervalMs: resolvedCaptureTuning.minSortIntervalMs,\n target: {\n width,\n height,\n },\n }),\n width,\n height,\n };\n captureSparkRef.current = captureSpark;\n }\n\n const captureRenderer = captureSpark.renderer;\n const maxWarmupFrames = Math.max(\n 1,\n Math.floor(resolvedCaptureTuning.maxWarmupFrames)\n );\n let pixels: Uint8Array | undefined;\n for (let attempt = 0; attempt < maxWarmupFrames; attempt += 1) {\n captureRenderer.renderSize.set(width, height);\n await captureRenderer.update({ scene, camera });\n pixels = await captureRenderer.renderReadTarget({\n scene,\n camera,\n });\n if (\n captureRenderer.activeSplats > 0 &&\n !isMostlyBlankCapture(pixels, resolvedCaptureTuning)\n ) {\n break;\n }\n if (attempt < maxWarmupFrames - 1) {\n await waitForNextAnimationFrame();\n }\n }\n if (!pixels) {\n throw new Error('Spark camera frame capture did not produce pixels.');\n }\n return { pixels, width, height, flipY: true };\n };\n const mesh = new sparkModule.SplatMesh({\n url: metadata.src,\n lod,\n });\n mesh.name = 'GaussianSplatMesh';\n\n groupRef.current.add(spark);\n groupRef.current.add(mesh);\n sparkRef.current = spark;\n meshRef.current = mesh;\n\n if (hideGroundMeshes && groupRef.current.parent) {\n groupRef.current.parent.traverse((object) => {\n if (\n !(object instanceof THREE.Mesh) ||\n object === (mesh as unknown as THREE.Object3D)\n ) {\n return;\n }\n const name = object.name.toLowerCase();\n if (\n name.includes('floor') ||\n name.includes('ground') ||\n name.includes('plane')\n ) {\n object.visible = false;\n hiddenMeshesRef.current.push(object);\n }\n });\n }\n\n await mesh.initialized;\n if (disposed) return;\n setLifecycleStatus('ready');\n onLoadRef.current?.(mesh);\n invalidate();\n } catch (error) {\n const normalizedError =\n error instanceof Error ? error : new Error(String(error));\n setLifecycleStatus('error');\n onErrorRef.current?.(normalizedError);\n }\n }\n\n void loadSplat();\n\n return () => {\n disposed = true;\n restoreHiddenMeshes();\n\n if (meshRef.current) {\n groupRef.current?.remove(meshRef.current);\n safelyDisposeSparkResource(meshRef.current);\n meshRef.current = null;\n }\n\n if (sparkRef.current) {\n groupRef.current?.remove(sparkRef.current);\n safelyDisposeSparkResource(sparkRef.current);\n sparkRef.current = null;\n }\n\n disposeCaptureSpark();\n };\n }, [\n gl,\n hideGroundMeshes,\n invalidate,\n lod,\n metadata.format,\n metadata.src,\n resolvedCaptureTuning.blankAlphaThreshold,\n resolvedCaptureTuning.blankAverageColor,\n resolvedCaptureTuning.blankSampleCount,\n resolvedCaptureTuning.blankVisibleRatio,\n resolvedCaptureTuning.lodRenderScale,\n resolvedCaptureTuning.lodSplatScale,\n resolvedCaptureTuning.maxWarmupFrames,\n resolvedCaptureTuning.minSortIntervalMs,\n resolvedRenderTuning.lodRenderScale,\n resolvedRenderTuning.lodSplatScale,\n resolvedRenderTuning.minSortIntervalMs,\n ]);\n\n return (\n <SplatEnvironment\n {...groupProps}\n environment={environment}\n scenario={scenario}\n renderer={renderer}\n src={metadata.src}\n format={metadata.format}\n collisionProxyMetadata={metadata.collisionProxy}\n collisionProxy={collisionProxy}\n showPlaceholder={showPlaceholder ?? status !== 'ready'}\n >\n <group ref={groupRef} />\n {children}\n </SplatEnvironment>\n );\n}\n\nfunction safelyDisposeSparkResource(\n resource: SparkDisposable | null | undefined\n) {\n if (!resource) return;\n try {\n silenceSparkWorkerTerminateRejections(resource);\n const result = resource.dispose?.();\n if (isPromiseLike(result)) {\n void Promise.resolve(result).catch(handleSparkDisposeError);\n }\n } catch (error) {\n handleSparkDisposeError(error);\n }\n}\n\nfunction isPromiseLike(value: unknown): value is PromiseLike<unknown> {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'then' in value &&\n typeof (value as { then?: unknown }).then === 'function'\n );\n}\n\nfunction silenceSparkWorkerTerminateRejections(resource: SparkDisposable) {\n const workers = getSparkWorkers(resource);\n for (const worker of workers) {\n if (!worker.messages) continue;\n\n for (const message of Object.values(worker.messages)) {\n const reject = message.reject;\n if (!reject) continue;\n\n message.reject = (error: unknown) => {\n if (!isSparkWorkerTerminateError(error)) {\n reject(error);\n }\n };\n }\n }\n}\n\nfunction getSparkWorkers(resource: SparkDisposable): SparkWorkerLike[] {\n const sparkResource = resource as SparkResourceWithWorkers;\n return [\n sparkResource.worker,\n sparkResource.sortWorker,\n sparkResource.lodWorker,\n ].filter((worker): worker is SparkWorkerLike => Boolean(worker));\n}\n\nfunction handleSparkDisposeError(error: unknown) {\n if (\n error instanceof Error &&\n error.message.toLowerCase().includes('worker terminate')\n ) {\n return;\n }\n\n console.warn('[mujoco-react] Spark resource disposal failed.', error);\n}\n\nfunction ensureSparkDisposeRejectionHandler() {\n if (\n sparkDisposeRejectionHandlerRegistered ||\n typeof window === 'undefined' ||\n typeof window.addEventListener !== 'function'\n ) {\n return;\n }\n\n sparkDisposeRejectionHandlerRegistered = true;\n window.addEventListener('unhandledrejection', (event) => {\n if (isSparkWorkerTerminateError(event.reason)) {\n event.preventDefault();\n }\n });\n}\n\nfunction isSparkWorkerTerminateError(reason: unknown) {\n return (\n reason instanceof Error &&\n reason.message.toLowerCase().includes('worker terminate')\n );\n}\n"]}
|
|
@@ -383,6 +383,14 @@ interface IkConfig {
|
|
|
383
383
|
ikSolveFn?: IKSolveFn;
|
|
384
384
|
/** DLS damping. Default: 0.01. */
|
|
385
385
|
damping?: number;
|
|
386
|
+
/** Position error weight for the built-in DLS solver. Default: 1. */
|
|
387
|
+
posWeight?: number;
|
|
388
|
+
/** Orientation error weight for the built-in DLS solver. Default: 0.3. */
|
|
389
|
+
rotWeight?: number;
|
|
390
|
+
/** Solver convergence tolerance. Default: 1e-3. */
|
|
391
|
+
tolerance?: number;
|
|
392
|
+
/** Finite-difference step used by the built-in DLS solver. Default: 1e-6. */
|
|
393
|
+
epsilon?: number;
|
|
386
394
|
/** Max solver iterations. Default: 50. */
|
|
387
395
|
maxIterations?: number;
|
|
388
396
|
}
|
|
@@ -643,6 +651,7 @@ interface DebugProps {
|
|
|
643
651
|
showGeoms?: boolean;
|
|
644
652
|
showSites?: boolean;
|
|
645
653
|
showJoints?: boolean;
|
|
654
|
+
showCameras?: boolean;
|
|
646
655
|
showContacts?: boolean;
|
|
647
656
|
showCOM?: boolean;
|
|
648
657
|
showInertia?: boolean;
|
|
@@ -658,6 +667,33 @@ interface IkGizmoDragInput {
|
|
|
658
667
|
position: THREE.Vector3;
|
|
659
668
|
quaternion: THREE.Quaternion;
|
|
660
669
|
}
|
|
670
|
+
type KeyboardIkTargetAction = 'x+' | 'x-' | 'y+' | 'y-' | 'z+' | 'z-' | 'pitch+' | 'pitch-' | 'yaw+' | 'yaw-' | 'roll+' | 'roll-';
|
|
671
|
+
interface KeyboardIkTargetBinding {
|
|
672
|
+
/** KeyboardEvent.code, e.g. `KeyW`, `ArrowUp`, `Space`. */
|
|
673
|
+
code: string;
|
|
674
|
+
action: KeyboardIkTargetAction;
|
|
675
|
+
/** Override translation speed in meters/second for this binding. */
|
|
676
|
+
translateSpeed?: number;
|
|
677
|
+
/** Override rotation speed in radians/second for this binding. */
|
|
678
|
+
rotateSpeed?: number;
|
|
679
|
+
}
|
|
680
|
+
interface KeyboardIkTargetConfig {
|
|
681
|
+
controller: IkContextValue | null;
|
|
682
|
+
bindings: KeyboardIkTargetBinding[];
|
|
683
|
+
enabled?: boolean;
|
|
684
|
+
/** Default translation speed in meters/second. Default: 0.25. */
|
|
685
|
+
translateSpeed?: number;
|
|
686
|
+
/** Default rotation speed in radians/second. Default: 1.0. */
|
|
687
|
+
rotateSpeed?: number;
|
|
688
|
+
/** Apply translation and rotation axes in world or current target space. Default: `world`. */
|
|
689
|
+
frame?: 'world' | 'target';
|
|
690
|
+
/** Enable IK while keys are active. Default: true. */
|
|
691
|
+
autoEnableIk?: boolean;
|
|
692
|
+
/** Sync target to current site when keyboard control starts. Default: true. */
|
|
693
|
+
syncOnStart?: boolean;
|
|
694
|
+
/** Prevent browser default behavior for bound keys. Default: true. */
|
|
695
|
+
preventDefault?: boolean;
|
|
696
|
+
}
|
|
661
697
|
interface DragInteractionProps {
|
|
662
698
|
stiffness?: number;
|
|
663
699
|
showArrow?: boolean;
|
|
@@ -1164,4 +1200,4 @@ interface JointStateResult {
|
|
|
1164
1200
|
velocity: React__default.RefObject<number | Float64Array>;
|
|
1165
1201
|
}
|
|
1166
1202
|
|
|
1167
|
-
export { type Bodies as $, type ActuatedJointInfo as A, type BodyProps as B, type ControlGroupInfo as C, type DragInteractionProps as D, type ScenarioLightingPreset as E, type SplatEnvironmentMetadataInput as F, type SplatEnvironmentMetadata as G, type SplatSceneInput as H, type IkConfig as I, type DebugProps as J, type GeomInfo as K, type ContactListenerProps as L, type MujocoContextValue as M, type ActuatorInfo as N, type ObservationConfig as O, type PhysicsStepCallback as P, type Sites as Q, type ReadyCallbackInput as R, type SceneConfig as S, type TrajectoryPlayerProps as T, type SitePositionResult as U, type VisualScenarioEffectsProps as V, type Sensors as W, type SensorHandle as X, type SensorInfo as Y, type Joints as Z, type JointStateResult as _, type MujocoCanvasProps as a,
|
|
1203
|
+
export { type Bodies as $, type ActuatedJointInfo as A, type BodyProps as B, type ControlGroupInfo as C, type DragInteractionProps as D, type ScenarioLightingPreset as E, type SplatEnvironmentMetadataInput as F, type SplatEnvironmentMetadata as G, type SplatSceneInput as H, type IkConfig as I, type DebugProps as J, type GeomInfo as K, type ContactListenerProps as L, type MujocoContextValue as M, type ActuatorInfo as N, type ObservationConfig as O, type PhysicsStepCallback as P, type Sites as Q, type ReadyCallbackInput as R, type SceneConfig as S, type TrajectoryPlayerProps as T, type SitePositionResult as U, type VisualScenarioEffectsProps as V, type Sensors as W, type SensorHandle as X, type SensorInfo as Y, type Joints as Z, type JointStateResult as _, type MujocoCanvasProps as a, type ResolvedScenarioCameraConfig as a$, type BodyStateResult as a0, type Actuators as a1, type CtrlHandle as a2, type ContactInfo as a3, type KeyboardTeleopConfig as a4, type KeyboardIkTargetConfig as a5, type PolicyConfig as a6, type PolicyVector as a7, type ObservationHandle as a8, type TrajectoryInput as a9, type FrameCaptureStatus as aA, type FrameCaptureTarget as aB, type FrameCaptureTargetRef as aC, type Geoms as aD, type IKSolveFn as aE, type IkGizmoDragInput as aF, type IkSolveInput as aG, type JointInfo as aH, type KeyBinding as aI, type KeyboardIkTargetAction as aJ, type KeyboardIkTargetBinding as aK, type Keyframes as aL, type ModelOptions as aM, type MujocoContact as aN, type MujocoContactArray as aO, type MujocoFrameCaptureOptions as aP, type ObservationLayoutItem as aQ, type ObservationOutput as aR, type PhysicsConfig as aS, type PhysicsStepInput as aT, type PolicyActionInput as aU, type PolicyInferenceInput as aV, type PolicyObservationInput as aW, type RayHit as aX, type Register as aY, type RegisteredRobotMap as aZ, type ResetCallbackInput as a_, type TrajectoryStateChangeInput as aa, type PlaybackState as ab, type TrajectoryFrame as ac, type FrameCaptureOptions as ad, type FrameCaptureResult as ae, type FrameCaptureBlobResult as af, type FrameCaptureAPI as ag, type CameraFrameCaptureOptions as ah, type CameraFrameCaptureAPI as ai, type CameraFrameSequenceRecorderAPI as aj, type Cameras as ak, type CameraFrameSequenceCamera as al, type CameraFrameCaptureSource as am, type CameraFrameSequenceOptions as an, type CameraFrameSequenceResult as ao, type CameraFrameCaptureResult as ap, type CameraFrameCaptureBlobResult as aq, type BodyInfo as ar, type CameraFrameCaptureQuaternion as as, type CameraFrameCaptureVector3 as at, type CameraFrameSequenceCameraSummary as au, type CameraFrameSequenceFrame as av, type CameraFrameSequenceSampleInput as aw, type CameraFrameSequenceStepInput as ax, type CameraInfo as ay, type ControlJointInfo as az, type MujocoSimAPI as b, type ResolvedScenarioMaterialConfig as b0, type ResourceSelector as b1, RobotActuators as b2, RobotBodies as b3, RobotCameras as b4, RobotGeoms as b5, RobotJoints as b6, RobotKeyframes as b7, type RobotResource as b8, RobotResources as b9, RobotSensors as ba, RobotSites as bb, type Robots as bc, type ScenarioCameraConfig as bd, type ScenarioMaterialConfig as be, type SceneMarker as bf, type SceneObject as bg, type SensorResult as bh, type SiteInfo as bi, type SplatAssetConfig as bj, type SplatScenarioConfig as bk, type StateSnapshot as bl, type TrajectoryData as bm, type TrajectoryFrameCallbackInput as bn, type VisualScenarioMaterialFilterInput as bo, type XmlPatch as bp, getContact as bq, registerRobotResources as br, type StepCallbackInput as c, type SelectionCallbackInput as d, type MujocoModule as e, type MujocoModel as f, type MujocoData as g, type ControlGroupSelector as h, type ObservationResult as i, type IkContextValue as j, type IkGizmoProps as k, type SceneLightsProps as l, type ScenarioLightingProps as m, type SplatEnvironmentProps as n, type VisualScenarioConfig as o, type SplatRendererKind as p, type PairedSplatEnvironmentConfig as q, type SplatFormat as r, type SplatCollisionProxyConfig as s, type SplatEnvironmentReadiness as t, type SplatCollisionPrimitive as u, SplatEnvironmentReadinessStatus as v, type SplatSceneConfigInput as w, type SplatSceneConfigState as x, type VisualScenarioExecutionContextInput as y, type VisualScenarioExecutionContext as z };
|
package/package.json
CHANGED
|
@@ -13,6 +13,7 @@ import { useFrame } from '@react-three/fiber';
|
|
|
13
13
|
import type { ThreeElements } from '@react-three/fiber';
|
|
14
14
|
import * as THREE from 'three';
|
|
15
15
|
import { useMujocoContext } from '../core/MujocoSimProvider';
|
|
16
|
+
import { CAPTURE_EXCLUDE_KEY } from '../rendering/cameraFrameCapture';
|
|
16
17
|
import { getContact, withContacts } from '../types';
|
|
17
18
|
|
|
18
19
|
const _dummy = new THREE.Object3D();
|
|
@@ -70,7 +71,13 @@ export function ContactMarkers({
|
|
|
70
71
|
if (status !== 'ready') return null;
|
|
71
72
|
|
|
72
73
|
return (
|
|
73
|
-
<group
|
|
74
|
+
<group
|
|
75
|
+
{...groupProps}
|
|
76
|
+
userData={{
|
|
77
|
+
...groupProps.userData,
|
|
78
|
+
[CAPTURE_EXCLUDE_KEY]: true,
|
|
79
|
+
}}
|
|
80
|
+
>
|
|
74
81
|
<instancedMesh ref={meshRef} args={[undefined, undefined, maxContacts]} frustumCulled={false} renderOrder={999}>
|
|
75
82
|
<sphereGeometry args={[radius, 8, 8]} />
|
|
76
83
|
<meshBasicMaterial color={color} depthTest={false} />
|