mujoco-react 10.2.1 → 10.4.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 +23 -1
- package/dist/{chunk-CYDGWNKQ.js → chunk-FBXXXPLQ.js} +169 -22
- package/dist/chunk-FBXXXPLQ.js.map +1 -0
- package/dist/index.d.ts +5 -4
- package/dist/index.js +345 -40
- package/dist/index.js.map +1 -1
- package/dist/spark.d.ts +1 -1
- package/dist/spark.js +4 -10
- package/dist/spark.js.map +1 -1
- package/dist/{types-B-J8fpPP.d.ts → types-CdFZCYmy.d.ts} +115 -1
- package/package.json +1 -1
- package/src/components/Debug.tsx +174 -3
- package/src/components/SceneRenderer.tsx +25 -6
- package/src/core/MujocoCanvas.tsx +8 -4
- package/src/core/MujocoPhysics.tsx +6 -4
- package/src/core/MujocoProvider.tsx +6 -4
- package/src/core/MujocoSimProvider.tsx +189 -9
- package/src/index.ts +1 -0
- package/src/rendering/GeomBuilder.ts +18 -2
- package/src/rendering/cameraFrameCapture.ts +229 -19
- package/src/spark.tsx +4 -12
- package/src/types.ts +121 -0
- package/dist/chunk-CYDGWNKQ.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 {
|
|
3
|
+
import { o as SplatEnvironmentProps, r as PairedSplatEnvironmentConfig, S as SceneConfig, u as SplatEnvironmentReadiness, p as VisualScenarioConfig } from './types-CdFZCYmy.js';
|
|
4
4
|
import 'react';
|
|
5
5
|
import '@react-three/fiber';
|
|
6
6
|
import 'three';
|
package/dist/spark.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useSplatSceneConfig, useSplatEnvironment, SplatEnvironment, CAMERA_FRAME_CAPTURE_RENDER_USER_DATA_KEY } from './chunk-
|
|
1
|
+
import { useSplatSceneConfig, useSplatEnvironment, SplatEnvironment, CAMERA_FRAME_CAPTURE_RENDER_USER_DATA_KEY } from './chunk-FBXXXPLQ.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';
|
|
@@ -190,6 +190,9 @@ function SparkSplatEnvironment({
|
|
|
190
190
|
const onErrorRef = useRef(onError);
|
|
191
191
|
const [status, setStatus] = useState("idle");
|
|
192
192
|
const { gl, invalidate } = useThree();
|
|
193
|
+
onStatusChangeRef.current = onStatusChange;
|
|
194
|
+
onLoadRef.current = onLoad;
|
|
195
|
+
onErrorRef.current = onError;
|
|
193
196
|
const metadata = useSplatEnvironment({
|
|
194
197
|
environment,
|
|
195
198
|
scenario,
|
|
@@ -221,15 +224,6 @@ function SparkSplatEnvironment({
|
|
|
221
224
|
lod
|
|
222
225
|
]
|
|
223
226
|
);
|
|
224
|
-
useEffect(() => {
|
|
225
|
-
onStatusChangeRef.current = onStatusChange;
|
|
226
|
-
}, [onStatusChange]);
|
|
227
|
-
useEffect(() => {
|
|
228
|
-
onLoadRef.current = onLoad;
|
|
229
|
-
}, [onLoad]);
|
|
230
|
-
useEffect(() => {
|
|
231
|
-
onErrorRef.current = onError;
|
|
232
|
-
}, [onError]);
|
|
233
227
|
useEffect(() => {
|
|
234
228
|
let disposed = false;
|
|
235
229
|
ensureSparkDisposeRejectionHandler();
|
package/dist/spark.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/spark.tsx"],"names":[],"mappings":";;;;;;AA2DA,IAAI,sCAAA,GAAyC,KAAA;AAoC7C,SAAS,2BACP,GAAA,EACwB;AACxB,EAAA,OAAO;AAAA,IACL,eAAe,GAAA,KAAQ,KAAA,GAAQ,MAAA,GAAY,GAAA,KAAQ,YAAY,CAAA,GAAI,GAAA;AAAA,IACnE,cAAA,EAAgB,GAAA,KAAQ,KAAA,IAAS,GAAA,KAAQ,YAAY,MAAA,GAAY,GAAA;AAAA,IACjE,mBAAmB,GAAA,KAAQ,KAAA,GAAQ,CAAA,GAAI,GAAA,KAAQ,YAAY,EAAA,GAAK;AAAA,GAClE;AACF;AAEA,SAAS,wBACP,GAAA,EACiC;AACjC,EAAA,OAAO;AAAA,IACL,eAAe,GAAA,KAAQ,KAAA,GAAQ,MAAA,GAAY,GAAA,KAAQ,YAAY,IAAA,GAAO,IAAA;AAAA,IACtE,cAAA,EAAgB,GAAA,KAAQ,KAAA,GAAQ,MAAA,GAAY,GAAA;AAAA,IAC5C,iBAAA,EAAmB,CAAA;AAAA,IACnB,eAAA,EAAiB,CAAA;AAAA,IACjB,gBAAA,EAAkB,GAAA;AAAA,IAClB,mBAAA,EAAqB,CAAA;AAAA,IACrB,iBAAA,EAAmB,IAAA;AAAA,IACnB,iBAAA,EAAmB;AAAA,GACrB;AACF;AAEA,SAAS,mBAAA,CACP,UACA,MAAA,EACwB;AACxB,EAAA,OAAO;AAAA,IACL,aAAA,EAAe,MAAA,EAAQ,aAAA,IAAiB,QAAA,CAAS,aAAA;AAAA,IACjD,cAAA,EAAgB,MAAA,EAAQ,cAAA,IAAkB,QAAA,CAAS,cAAA;AAAA,IACnD,iBAAA,EAAmB,MAAA,EAAQ,iBAAA,IAAqB,QAAA,CAAS;AAAA,GAC3D;AACF;AAEA,SAAS,oBAAA,CACP,KACA,MAAA,EACiC;AACjC,EAAA,MAAM,QAAA,GAAW,wBAAwB,GAAG,CAAA;AAC5C,EAAA,OAAO;AAAA,IACL,GAAG,mBAAA,CAAoB,QAAA,EAAU,MAAM,CAAA;AAAA,IACvC,eAAA,EAAiB,MAAA,EAAQ,eAAA,IAAmB,QAAA,CAAS,eAAA;AAAA,IACrD,gBAAA,EAAkB,MAAA,EAAQ,gBAAA,IAAoB,QAAA,CAAS,gBAAA;AAAA,IACvD,mBAAA,EACE,MAAA,EAAQ,mBAAA,IAAuB,QAAA,CAAS,mBAAA;AAAA,IAC1C,iBAAA,EAAmB,MAAA,EAAQ,iBAAA,IAAqB,QAAA,CAAS,iBAAA;AAAA,IACzD,iBAAA,EAAmB,MAAA,EAAQ,iBAAA,IAAqB,QAAA,CAAS;AAAA,GAC3D;AACF;AAEA,SAAS,yBAAA,GAA4B;AACnC,EAAA,OAAO,IAAI,OAAA,CAAc,CAAC,OAAA,KAAY;AACpC,IAAA,qBAAA,CAAsB,MAAM,SAAS,CAAA;AAAA,EACvC,CAAC,CAAA;AACH;AAEA,SAAS,oBAAA,CACP,QACA,MAAA,EACA;AACA,EAAA,MAAM,WAAA,GAAc,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,KAAA,CAAM,MAAA,CAAO,gBAAgB,CAAC,CAAA;AACnE,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,KAAA,CAAM,MAAA,CAAO,MAAA,GAAS,WAAA,GAAc,CAAC,CAAA,GAAI,CAAC,CAAA;AAC1E,EAAA,IAAI,OAAA,GAAU,CAAA;AACd,EAAA,IAAI,OAAA,GAAU,CAAA;AACd,EAAA,IAAI,UAAA,GAAa,CAAA;AAEjB,EAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,MAAA,CAAO,MAAA,EAAQ,KAAK,MAAA,EAAQ;AAC9C,IAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,CAAA,GAAI,CAAC,CAAA;AAC1B,IAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,CAAC,CAAA,GAAI,MAAA,CAAO,IAAI,CAAC,CAAA,GAAI,MAAA,CAAO,CAAA,GAAI,CAAC,CAAA;AACtD,IAAA,OAAA,IAAW,CAAA;AACX,IAAA,IAAI,KAAA,GAAQ,MAAA,CAAO,mBAAA,EAAqB,OAAA,IAAW,CAAA;AACnD,IAAA,UAAA,IAAc,KAAA;AAAA,EAChB;AAEA,EAAA,IAAI,OAAA,KAAY,GAAG,OAAO,IAAA;AAC1B,EAAA,OACE,UAAU,OAAA,GAAU,MAAA,CAAO,iBAAA,IAC3B,UAAA,GAAa,UAAU,MAAA,CAAO,iBAAA;AAElC;AA+CO,SAAS,wBAAA,CAAyB;AAAA,EACvC,WAAA;AAAA,EACA,QAAA;AAAA,EACA,WAAA;AAAA,EACA,OAAA,GAAU,IAAA;AAAA,EACV,QAAA,GAAW,OAAA;AAAA,EACX,OAAA;AAAA,EACA;AACF,CAAA,EAQ+B;AAC7B,EAAA,MAAM,aAAa,mBAAA,CAAoB;AAAA,IACrC,WAAA;AAAA,IACA,QAAA;AAAA,IACA,WAAA;AAAA,IACA,OAAA;AAAA,IACA;AAAA,GACD,CAAA;AACD,EAAA,MAAM,WAAW,mBAAA,CAAoB;AAAA,IACnC,QAAA;AAAA,IACA,aAAa,UAAA,CAAW,WAAA;AAAA,IACxB;AAAA,GACD,CAAA;AACD,EAAA,MAAM,aAAA,GAAgB,OAAA,IAAW,OAAA,CAAQ,QAAA,CAAS,GAAG,CAAA;AACrD,EAAA,MAAM,SAAA,GAAY,OAAA,GAAU,QAAA,CAAS,SAAA,GAAY,UAAA,CAAW,SAAA;AAC5D,EAAA,MAAM,YAAY,sBAAA,CAAuB;AAAA,IACvC,OAAA,EAAS,aAAA;AAAA,IACT,OAAA;AAAA,IACA;AAAA,GACD,CAAA;AAED,EAAA,OAAO,OAAA;AAAA,IACL,OAAO;AAAA,MACL,aAAa,UAAA,CAAW,WAAA;AAAA,MACxB,aAAa,UAAA,CAAW,WAAA;AAAA,MACxB,SAAA;AAAA,MACA,SAAA;AAAA,MACA,KAAA,EAAO;AAAA,QACL,aAAa,UAAA,CAAW,WAAA;AAAA,QACxB,QAAA,EAAU,UAAU,QAAA,GAAW,MAAA;AAAA,QAC/B,GAAA,EAAK,OAAA,GAAU,QAAA,CAAS,GAAA,GAAM,MAAA;AAAA,QAC9B,QAAQ,QAAA,CAAS,MAAA;AAAA,QACjB,GAAG,SAAA,CAAU;AAAA,OACf;AAAA,MACA,OAAA,EAAS;AAAA,KACX,CAAA;AAAA,IACA,CAAC,OAAA,EAAS,SAAA,EAAW,UAAU,SAAA,EAAW,aAAA,EAAe,UAAU,UAAU;AAAA,GAC/E;AACF;AAQO,SAAS,sBAAA,CAAuB;AAAA,EACrC,OAAA,GAAU,IAAA;AAAA,EACV,aAAA;AAAA,EACA,OAAA;AAAA,EACA;AACF,CAAA,GAKI,EAAC,EAAwB;AAC3B,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAI,QAAA;AAAA,IAC1B,aAAA,KAAkB,UAAU,SAAA,GAAY,MAAA;AAAA,GAC1C;AACA,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,SAAuB,IAAI,CAAA;AAErD,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,SAAA,CAAU,OAAA,GAAU,aAAA,IAAiB,SAAA,GAAY,MAAM,CAAA;AACvD,IAAA,QAAA,CAAS,IAAI,CAAA;AAAA,EACf,CAAA,EAAG,CAAC,OAAA,EAAS,aAAa,CAAC,CAAA;AAE3B,EAAA,MAAM,kBAAA,GAAqB,WAAA;AAAA,IACzB,CAAC,UAAA,KAAiC;AAChC,MAAA,SAAA,CAAU,UAAU,CAAA;AACpB,MAAA,IAAI,eAAe,OAAA,EAAS;AAC1B,QAAA,QAAA,CAAS,IAAI,CAAA;AAAA,MACf;AACA,MAAA,cAAA,GAAiB,UAAU,CAAA;AAAA,IAC7B,CAAA;AAAA,IACA,CAAC,cAAc;AAAA,GACjB;AAEA,EAAA,MAAM,WAAA,GAAc,WAAA;AAAA,IAClB,CAAC,SAAA,KAAqB;AACpB,MAAA,QAAA,CAAS,SAAS,CAAA;AAClB,MAAA,SAAA,CAAU,OAAO,CAAA;AACjB,MAAA,OAAA,GAAU,SAAS,CAAA;AAAA,IACrB,CAAA;AAAA,IACA,CAAC,OAAO;AAAA,GACV;AAEA,EAAA,MAAM,KAAA,GAAQ,YAAY,MAAM;AAC9B,IAAA,SAAA,CAAU,OAAA,GAAU,aAAA,IAAiB,SAAA,GAAY,MAAM,CAAA;AACvD,IAAA,QAAA,CAAS,IAAI,CAAA;AAAA,EACf,CAAA,EAAG,CAAC,OAAA,EAAS,aAAa,CAAC,CAAA;AAE3B,EAAA,OAAO,OAAA;AAAA,IACL,OAAO;AAAA,MACL,MAAA;AAAA,MACA,KAAA;AAAA,MACA,WAAW,MAAA,KAAW,SAAA;AAAA,MACtB,SAAS,MAAA,KAAW,OAAA;AAAA,MACpB,SAAS,MAAA,KAAW,OAAA;AAAA,MACpB,KAAA,EAAO;AAAA,QACL,cAAA,EAAgB,kBAAA;AAAA,QAChB,OAAA,EAAS;AAAA,OACX;AAAA,MACA;AAAA,KACF,CAAA;AAAA,IACA,CAAC,KAAA,EAAO,WAAA,EAAa,kBAAA,EAAoB,OAAO,MAAM;AAAA,GACxD;AACF;AAQO,SAAS,qBAAA,CAAsB;AAAA,EACpC,WAAA;AAAA,EACA,QAAA;AAAA,EACA,QAAA,GAAW,OAAA;AAAA,EACX,GAAA;AAAA,EACA,MAAA;AAAA,EACA,cAAA;AAAA,EACA,sBAAA;AAAA,EACA,eAAA;AAAA,EACA,QAAA;AAAA,EACA,GAAA,GAAM,IAAA;AAAA,EACN,YAAA;AAAA,EACA,aAAA;AAAA,EACA,gBAAA,GAAmB,KAAA;AAAA,EACnB,cAAA;AAAA,EACA,MAAA;AAAA,EACA,OAAA;AAAA,EACA,GAAG;AACL,CAAA,EAA+B;AAC7B,EAAA,MAAM,QAAA,GAAW,OAAoB,IAAI,CAAA;AACzC,EAAA,MAAM,QAAA,GAAW,OAAqC,IAAI,CAAA;AAC1D,EAAA,MAAM,eAAA,GAAkB,OAAoC,IAAI,CAAA;AAChE,EAAA,MAAM,OAAA,GAAU,OAAsC,IAAI,CAAA;AAC1D,EAAA,MAAM,eAAA,GAAkB,MAAA,CAAqB,EAAE,CAAA;AAC/C,EAAA,MAAM,iBAAA,GAAoB,OAAO,cAAc,CAAA;AAC/C,EAAA,MAAM,SAAA,GAAY,OAAO,MAAM,CAAA;AAC/B,EAAA,MAAM,UAAA,GAAa,OAAO,OAAO,CAAA;AACjC,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAI,SAA2B,MAAM,CAAA;AAC7D,EAAA,MAAM,EAAE,EAAA,EAAI,UAAA,EAAW,GAAI,QAAA,EAAS;AACpC,EAAA,MAAM,WAAW,mBAAA,CAAoB;AAAA,IACnC,WAAA;AAAA,IACA,QAAA;AAAA,IACA,QAAA;AAAA,IACA,GAAA;AAAA,IACA,MAAA;AAAA,IACA,cAAA,EAAgB;AAAA,GACjB,CAAA;AACD,EAAA,MAAM,oBAAA,GAAuB,OAAA;AAAA,IAC3B,MAAM,mBAAA,CAAoB,0BAAA,CAA2B,GAAG,GAAG,YAAY,CAAA;AAAA,IACvE;AAAA,MACE,GAAA;AAAA,MACA,YAAA,EAAc,cAAA;AAAA,MACd,YAAA,EAAc,aAAA;AAAA,MACd,YAAA,EAAc;AAAA;AAChB,GACF;AACA,EAAA,MAAM,qBAAA,GAAwB,OAAA;AAAA,IAC5B,MAAM,oBAAA,CAAqB,GAAA,EAAK,aAAa,CAAA;AAAA,IAC7C;AAAA,MACE,aAAA,EAAe,mBAAA;AAAA,MACf,aAAA,EAAe,iBAAA;AAAA,MACf,aAAA,EAAe,gBAAA;AAAA,MACf,aAAA,EAAe,iBAAA;AAAA,MACf,aAAA,EAAe,cAAA;AAAA,MACf,aAAA,EAAe,aAAA;AAAA,MACf,aAAA,EAAe,eAAA;AAAA,MACf,aAAA,EAAe,iBAAA;AAAA,MACf;AAAA;AACF,GACF;AAEA,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,iBAAA,CAAkB,OAAA,GAAU,cAAA;AAAA,EAC9B,CAAA,EAAG,CAAC,cAAc,CAAC,CAAA;AAEnB,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,SAAA,CAAU,OAAA,GAAU,MAAA;AAAA,EACtB,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAEX,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,UAAA,CAAW,OAAA,GAAU,OAAA;AAAA,EACvB,CAAA,EAAG,CAAC,OAAO,CAAC,CAAA;AAEZ,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,QAAA,GAAW,KAAA;AACf,IAAA,kCAAA,EAAmC;AAEnC,IAAA,SAAS,mBAAmB,UAAA,EAA8B;AACxD,MAAA,SAAA,CAAU,UAAU,CAAA;AACpB,MAAA,iBAAA,CAAkB,UAAU,UAAU,CAAA;AAAA,IACxC;AAEA,IAAA,SAAS,mBAAA,GAAsB;AAC7B,MAAA,KAAA,MAAW,IAAA,IAAQ,gBAAgB,OAAA,EAAS;AAC1C,QAAA,IAAA,CAAK,OAAA,GAAU,IAAA;AAAA,MACjB;AACA,MAAA,eAAA,CAAgB,UAAU,EAAC;AAAA,IAC7B;AAEA,IAAA,SAAS,mBAAA,GAAsB;AAC7B,MAAA,0BAAA,CAA2B,eAAA,CAAgB,SAAS,QAAQ,CAAA;AAC5D,MAAA,eAAA,CAAgB,OAAA,GAAU,IAAA;AAAA,IAC5B;AAEA,IAAA,eAAe,SAAA,GAAY;AACzB,MAAA,IAAI,CAAC,SAAS,GAAA,EAAK;AACjB,QAAA,kBAAA,CAAmB,MAAM,CAAA;AACzB,QAAA;AAAA,MACF;AAEA,MAAA,IAAI,QAAA,CAAS,WAAW,KAAA,EAAO;AAC7B,QAAA,MAAM,yBAAyB,IAAI,KAAA;AAAA,UACjC,CAAA,2DAAA,EAA8D,SAAS,MAAM,CAAA,EAAA;AAAA,SAC/E;AACA,QAAA,kBAAA,CAAmB,OAAO,CAAA;AAC1B,QAAA,UAAA,CAAW,UAAU,sBAAsB,CAAA;AAC3C,QAAA;AAAA,MACF;AAEA,MAAA,kBAAA,CAAmB,SAAS,CAAA;AAE5B,MAAA,IAAI;AACF,QAAA,MAAM,WAAA,GAAc,MAAM,OAAO,mBAAmB,CAAA;AACpD,QAAA,IAAI,QAAA,IAAY,CAAC,QAAA,CAAS,OAAA,EAAS;AAEnC,QAAA,MAAM,KAAA,GAAQ,IAAI,WAAA,CAAY,aAAA,CAAc;AAAA,UAC1C,QAAA,EAAU,EAAA;AAAA,UACV,OAAA,EAAS,UAAA;AAAA,UACT,GAAG;AAAA,SACJ,CAAA;AACD,QAAA,KAAA,CAAM,QAAA,CAAS,yCAAyC,CAAA,GAAI,OAAO;AAAA,UACjE,KAAA;AAAA,UACA,MAAA;AAAA,UACA,KAAA;AAAA,UACA;AAAA,SACF,KAA8E;AAC5E,UAAA,IAAI,eAAe,eAAA,CAAgB,OAAA;AACnC,UAAA,IACE,CAAC,YAAA,IACD,YAAA,CAAa,UAAU,KAAA,IACvB,YAAA,CAAa,WAAW,MAAA,EACxB;AACA,YAAA,mBAAA,EAAoB;AACpB,YAAA,YAAA,GAAe;AAAA,cACb,QAAA,EAAU,IAAI,WAAA,CAAY,aAAA,CAAc;AAAA,gBACtC,QAAA,EAAU,EAAA;AAAA,gBACV,UAAA,EAAY,KAAA;AAAA,gBACZ,eAAe,qBAAA,CAAsB,aAAA;AAAA,gBACrC,gBAAgB,qBAAA,CAAsB,cAAA;AAAA,gBACtC,mBAAmB,qBAAA,CAAsB,iBAAA;AAAA,gBACzC,MAAA,EAAQ;AAAA,kBACN,KAAA;AAAA,kBACA;AAAA;AACF,eACD,CAAA;AAAA,cACD,KAAA;AAAA,cACA;AAAA,aACF;AACA,YAAA,eAAA,CAAgB,OAAA,GAAU,YAAA;AAAA,UAC5B;AAEA,UAAA,MAAM,kBAAkB,YAAA,CAAa,QAAA;AACrC,UAAA,MAAM,kBAAkB,IAAA,CAAK,GAAA;AAAA,YAC3B,CAAA;AAAA,YACA,IAAA,CAAK,KAAA,CAAM,qBAAA,CAAsB,eAAe;AAAA,WAClD;AACA,UAAA,IAAI,MAAA;AACJ,UAAA,KAAA,IAAS,OAAA,GAAU,CAAA,EAAG,OAAA,GAAU,eAAA,EAAiB,WAAW,CAAA,EAAG;AAC7D,YAAA,eAAA,CAAgB,UAAA,CAAW,GAAA,CAAI,KAAA,EAAO,MAAM,CAAA;AAC5C,YAAA,MAAM,eAAA,CAAgB,MAAA,CAAO,EAAE,KAAA,EAAO,QAAQ,CAAA;AAC9C,YAAA,MAAA,GAAS,MAAM,gBAAgB,gBAAA,CAAiB;AAAA,cAC9C,KAAA;AAAA,cACA;AAAA,aACD,CAAA;AACD,YAAA,IACE,gBAAgB,YAAA,GAAe,CAAA,IAC/B,CAAC,oBAAA,CAAqB,MAAA,EAAQ,qBAAqB,CAAA,EACnD;AACA,cAAA;AAAA,YACF;AACA,YAAA,IAAI,OAAA,GAAU,kBAAkB,CAAA,EAAG;AACjC,cAAA,MAAM,yBAAA,EAA0B;AAAA,YAClC;AAAA,UACF;AACA,UAAA,IAAI,CAAC,MAAA,EAAQ;AACX,YAAA,MAAM,IAAI,MAAM,oDAAoD,CAAA;AAAA,UACtE;AACA,UAAA,OAAO,EAAE,MAAA,EAAQ,KAAA,EAAO,MAAA,EAAQ,OAAO,IAAA,EAAK;AAAA,QAC9C,CAAA;AACA,QAAA,MAAM,IAAA,GAAO,IAAI,WAAA,CAAY,SAAA,CAAU;AAAA,UACrC,KAAK,QAAA,CAAS,GAAA;AAAA,UACd;AAAA,SACD,CAAA;AACD,QAAA,IAAA,CAAK,IAAA,GAAO,mBAAA;AAEZ,QAAA,QAAA,CAAS,OAAA,CAAQ,IAAI,KAAK,CAAA;AAC1B,QAAA,QAAA,CAAS,OAAA,CAAQ,IAAI,IAAI,CAAA;AACzB,QAAA,QAAA,CAAS,OAAA,GAAU,KAAA;AACnB,QAAA,OAAA,CAAQ,OAAA,GAAU,IAAA;AAElB,QAAA,IAAI,gBAAA,IAAoB,QAAA,CAAS,OAAA,CAAQ,MAAA,EAAQ;AAC/C,UAAA,QAAA,CAAS,OAAA,CAAQ,MAAA,CAAO,QAAA,CAAS,CAAC,MAAA,KAAW;AAC3C,YAAA,IACE,EAAE,MAAA,YAAwB,KAAA,CAAA,IAAA,CAAA,IAC1B,MAAA,KAAY,IAAA,EACZ;AACA,cAAA;AAAA,YACF;AACA,YAAA,MAAM,IAAA,GAAO,MAAA,CAAO,IAAA,CAAK,WAAA,EAAY;AACrC,YAAA,IACE,IAAA,CAAK,QAAA,CAAS,OAAO,CAAA,IACrB,IAAA,CAAK,QAAA,CAAS,QAAQ,CAAA,IACtB,IAAA,CAAK,QAAA,CAAS,OAAO,CAAA,EACrB;AACA,cAAA,MAAA,CAAO,OAAA,GAAU,KAAA;AACjB,cAAA,eAAA,CAAgB,OAAA,CAAQ,KAAK,MAAM,CAAA;AAAA,YACrC;AAAA,UACF,CAAC,CAAA;AAAA,QACH;AAEA,QAAA,MAAM,IAAA,CAAK,WAAA;AACX,QAAA,IAAI,QAAA,EAAU;AACd,QAAA,kBAAA,CAAmB,OAAO,CAAA;AAC1B,QAAA,SAAA,CAAU,UAAU,IAAI,CAAA;AACxB,QAAA,UAAA,EAAW;AAAA,MACb,SAAS,KAAA,EAAO;AACd,QAAA,MAAM,eAAA,GACJ,iBAAiB,KAAA,GAAQ,KAAA,GAAQ,IAAI,KAAA,CAAM,MAAA,CAAO,KAAK,CAAC,CAAA;AAC1D,QAAA,kBAAA,CAAmB,OAAO,CAAA;AAC1B,QAAA,UAAA,CAAW,UAAU,eAAe,CAAA;AAAA,MACtC;AAAA,IACF;AAEA,IAAA,KAAK,SAAA,EAAU;AAEf,IAAA,OAAO,MAAM;AACX,MAAA,QAAA,GAAW,IAAA;AACX,MAAA,mBAAA,EAAoB;AAEpB,MAAA,IAAI,QAAQ,OAAA,EAAS;AACnB,QAAA,QAAA,CAAS,OAAA,EAAS,MAAA,CAAO,OAAA,CAAQ,OAAO,CAAA;AACxC,QAAA,0BAAA,CAA2B,QAAQ,OAAO,CAAA;AAC1C,QAAA,OAAA,CAAQ,OAAA,GAAU,IAAA;AAAA,MACpB;AAEA,MAAA,IAAI,SAAS,OAAA,EAAS;AACpB,QAAA,QAAA,CAAS,OAAA,EAAS,MAAA,CAAO,QAAA,CAAS,OAAO,CAAA;AACzC,QAAA,0BAAA,CAA2B,SAAS,OAAO,CAAA;AAC3C,QAAA,QAAA,CAAS,OAAA,GAAU,IAAA;AAAA,MACrB;AAEA,MAAA,mBAAA,EAAoB;AAAA,IACtB,CAAA;AAAA,EACF,CAAA,EAAG;AAAA,IACD,EAAA;AAAA,IACA,gBAAA;AAAA,IACA,UAAA;AAAA,IACA,GAAA;AAAA,IACA,QAAA,CAAS,MAAA;AAAA,IACT,QAAA,CAAS,GAAA;AAAA,IACT,qBAAA,CAAsB,mBAAA;AAAA,IACtB,qBAAA,CAAsB,iBAAA;AAAA,IACtB,qBAAA,CAAsB,gBAAA;AAAA,IACtB,qBAAA,CAAsB,iBAAA;AAAA,IACtB,qBAAA,CAAsB,cAAA;AAAA,IACtB,qBAAA,CAAsB,aAAA;AAAA,IACtB,qBAAA,CAAsB,eAAA;AAAA,IACtB,qBAAA,CAAsB,iBAAA;AAAA,IACtB,oBAAA,CAAqB,cAAA;AAAA,IACrB,oBAAA,CAAqB,aAAA;AAAA,IACrB,oBAAA,CAAqB;AAAA,GACtB,CAAA;AAED,EAAA,uBACE,IAAA;AAAA,IAAC,gBAAA;AAAA,IAAA;AAAA,MACE,GAAG,UAAA;AAAA,MACJ,WAAA;AAAA,MACA,QAAA;AAAA,MACA,QAAA;AAAA,MACA,KAAK,QAAA,CAAS,GAAA;AAAA,MACd,QAAQ,QAAA,CAAS,MAAA;AAAA,MACjB,wBAAwB,QAAA,CAAS,cAAA;AAAA,MACjC,cAAA;AAAA,MACA,eAAA,EAAiB,mBAAmB,MAAA,KAAW,OAAA;AAAA,MAE/C,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,OAAA,EAAA,EAAM,KAAK,QAAA,EAAU,CAAA;AAAA,QACrB;AAAA;AAAA;AAAA,GACH;AAEJ;AAEA,SAAS,2BACP,QAAA,EACA;AACA,EAAA,IAAI,CAAC,QAAA,EAAU;AACf,EAAA,IAAI;AACF,IAAA,qCAAA,CAAsC,QAAQ,CAAA;AAC9C,IAAA,MAAM,MAAA,GAAS,SAAS,OAAA,IAAU;AAClC,IAAA,IAAI,aAAA,CAAc,MAAM,CAAA,EAAG;AACzB,MAAA,KAAK,OAAA,CAAQ,OAAA,CAAQ,MAAM,CAAA,CAAE,MAAM,uBAAuB,CAAA;AAAA,IAC5D;AAAA,EACF,SAAS,KAAA,EAAO;AACd,IAAA,uBAAA,CAAwB,KAAK,CAAA;AAAA,EAC/B;AACF;AAEA,SAAS,cAAc,KAAA,EAA+C;AACpE,EAAA,OACE,OAAO,UAAU,QAAA,IACjB,KAAA,KAAU,QACV,MAAA,IAAU,KAAA,IACV,OAAQ,KAAA,CAA6B,IAAA,KAAS,UAAA;AAElD;AAEA,SAAS,sCAAsC,QAAA,EAA2B;AACxE,EAAA,MAAM,OAAA,GAAU,gBAAgB,QAAQ,CAAA;AACxC,EAAA,KAAA,MAAW,UAAU,OAAA,EAAS;AAC5B,IAAA,IAAI,CAAC,OAAO,QAAA,EAAU;AAEtB,IAAA,KAAA,MAAW,OAAA,IAAW,MAAA,CAAO,MAAA,CAAO,MAAA,CAAO,QAAQ,CAAA,EAAG;AACpD,MAAA,MAAM,SAAS,OAAA,CAAQ,MAAA;AACvB,MAAA,IAAI,CAAC,MAAA,EAAQ;AAEb,MAAA,OAAA,CAAQ,MAAA,GAAS,CAAC,KAAA,KAAmB;AACnC,QAAA,IAAI,CAAC,2BAAA,CAA4B,KAAK,CAAA,EAAG;AACvC,UAAA,MAAA,CAAO,KAAK,CAAA;AAAA,QACd;AAAA,MACF,CAAA;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,gBAAgB,QAAA,EAA8C;AACrE,EAAA,MAAM,aAAA,GAAgB,QAAA;AACtB,EAAA,OAAO;AAAA,IACL,aAAA,CAAc,MAAA;AAAA,IACd,aAAA,CAAc,UAAA;AAAA,IACd,aAAA,CAAc;AAAA,IACd,MAAA,CAAO,CAAC,MAAA,KAAsC,OAAA,CAAQ,MAAM,CAAC,CAAA;AACjE;AAEA,SAAS,wBAAwB,KAAA,EAAgB;AAC/C,EAAA,IACE,KAAA,YAAiB,SACjB,KAAA,CAAM,OAAA,CAAQ,aAAY,CAAE,QAAA,CAAS,kBAAkB,CAAA,EACvD;AACA,IAAA;AAAA,EACF;AAEA,EAAA,OAAA,CAAQ,IAAA,CAAK,kDAAkD,KAAK,CAAA;AACtE;AAEA,SAAS,kCAAA,GAAqC;AAC5C,EAAA,IACE,0CACA,OAAO,MAAA,KAAW,eAClB,OAAO,MAAA,CAAO,qBAAqB,UAAA,EACnC;AACA,IAAA;AAAA,EACF;AAEA,EAAA,sCAAA,GAAyC,IAAA;AACzC,EAAA,MAAA,CAAO,gBAAA,CAAiB,oBAAA,EAAsB,CAAC,KAAA,KAAU;AACvD,IAAA,IAAI,2BAAA,CAA4B,KAAA,CAAM,MAAM,CAAA,EAAG;AAC7C,MAAA,KAAA,CAAM,cAAA,EAAe;AAAA,IACvB;AAAA,EACF,CAAC,CAAA;AACH;AAEA,SAAS,4BAA4B,MAAA,EAAiB;AACpD,EAAA,OACE,kBAAkB,KAAA,IAClB,MAAA,CAAO,QAAQ,WAAA,EAAY,CAAE,SAAS,kBAAkB,CAAA;AAE5D","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"]}
|
|
1
|
+
{"version":3,"sources":["../src/spark.tsx"],"names":[],"mappings":";;;;;;AA2DA,IAAI,sCAAA,GAAyC,KAAA;AAoC7C,SAAS,2BACP,GAAA,EACwB;AACxB,EAAA,OAAO;AAAA,IACL,eAAe,GAAA,KAAQ,KAAA,GAAQ,MAAA,GAAY,GAAA,KAAQ,YAAY,CAAA,GAAI,GAAA;AAAA,IACnE,cAAA,EAAgB,GAAA,KAAQ,KAAA,IAAS,GAAA,KAAQ,YAAY,MAAA,GAAY,GAAA;AAAA,IACjE,mBAAmB,GAAA,KAAQ,KAAA,GAAQ,CAAA,GAAI,GAAA,KAAQ,YAAY,EAAA,GAAK;AAAA,GAClE;AACF;AAEA,SAAS,wBACP,GAAA,EACiC;AACjC,EAAA,OAAO;AAAA,IACL,eAAe,GAAA,KAAQ,KAAA,GAAQ,MAAA,GAAY,GAAA,KAAQ,YAAY,IAAA,GAAO,IAAA;AAAA,IACtE,cAAA,EAAgB,GAAA,KAAQ,KAAA,GAAQ,MAAA,GAAY,GAAA;AAAA,IAC5C,iBAAA,EAAmB,CAAA;AAAA,IACnB,eAAA,EAAiB,CAAA;AAAA,IACjB,gBAAA,EAAkB,GAAA;AAAA,IAClB,mBAAA,EAAqB,CAAA;AAAA,IACrB,iBAAA,EAAmB,IAAA;AAAA,IACnB,iBAAA,EAAmB;AAAA,GACrB;AACF;AAEA,SAAS,mBAAA,CACP,UACA,MAAA,EACwB;AACxB,EAAA,OAAO;AAAA,IACL,aAAA,EAAe,MAAA,EAAQ,aAAA,IAAiB,QAAA,CAAS,aAAA;AAAA,IACjD,cAAA,EAAgB,MAAA,EAAQ,cAAA,IAAkB,QAAA,CAAS,cAAA;AAAA,IACnD,iBAAA,EAAmB,MAAA,EAAQ,iBAAA,IAAqB,QAAA,CAAS;AAAA,GAC3D;AACF;AAEA,SAAS,oBAAA,CACP,KACA,MAAA,EACiC;AACjC,EAAA,MAAM,QAAA,GAAW,wBAAwB,GAAG,CAAA;AAC5C,EAAA,OAAO;AAAA,IACL,GAAG,mBAAA,CAAoB,QAAA,EAAU,MAAM,CAAA;AAAA,IACvC,eAAA,EAAiB,MAAA,EAAQ,eAAA,IAAmB,QAAA,CAAS,eAAA;AAAA,IACrD,gBAAA,EAAkB,MAAA,EAAQ,gBAAA,IAAoB,QAAA,CAAS,gBAAA;AAAA,IACvD,mBAAA,EACE,MAAA,EAAQ,mBAAA,IAAuB,QAAA,CAAS,mBAAA;AAAA,IAC1C,iBAAA,EAAmB,MAAA,EAAQ,iBAAA,IAAqB,QAAA,CAAS,iBAAA;AAAA,IACzD,iBAAA,EAAmB,MAAA,EAAQ,iBAAA,IAAqB,QAAA,CAAS;AAAA,GAC3D;AACF;AAEA,SAAS,yBAAA,GAA4B;AACnC,EAAA,OAAO,IAAI,OAAA,CAAc,CAAC,OAAA,KAAY;AACpC,IAAA,qBAAA,CAAsB,MAAM,SAAS,CAAA;AAAA,EACvC,CAAC,CAAA;AACH;AAEA,SAAS,oBAAA,CACP,QACA,MAAA,EACA;AACA,EAAA,MAAM,WAAA,GAAc,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,KAAA,CAAM,MAAA,CAAO,gBAAgB,CAAC,CAAA;AACnE,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,KAAA,CAAM,MAAA,CAAO,MAAA,GAAS,WAAA,GAAc,CAAC,CAAA,GAAI,CAAC,CAAA;AAC1E,EAAA,IAAI,OAAA,GAAU,CAAA;AACd,EAAA,IAAI,OAAA,GAAU,CAAA;AACd,EAAA,IAAI,UAAA,GAAa,CAAA;AAEjB,EAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,MAAA,CAAO,MAAA,EAAQ,KAAK,MAAA,EAAQ;AAC9C,IAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,CAAA,GAAI,CAAC,CAAA;AAC1B,IAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,CAAC,CAAA,GAAI,MAAA,CAAO,IAAI,CAAC,CAAA,GAAI,MAAA,CAAO,CAAA,GAAI,CAAC,CAAA;AACtD,IAAA,OAAA,IAAW,CAAA;AACX,IAAA,IAAI,KAAA,GAAQ,MAAA,CAAO,mBAAA,EAAqB,OAAA,IAAW,CAAA;AACnD,IAAA,UAAA,IAAc,KAAA;AAAA,EAChB;AAEA,EAAA,IAAI,OAAA,KAAY,GAAG,OAAO,IAAA;AAC1B,EAAA,OACE,UAAU,OAAA,GAAU,MAAA,CAAO,iBAAA,IAC3B,UAAA,GAAa,UAAU,MAAA,CAAO,iBAAA;AAElC;AA+CO,SAAS,wBAAA,CAAyB;AAAA,EACvC,WAAA;AAAA,EACA,QAAA;AAAA,EACA,WAAA;AAAA,EACA,OAAA,GAAU,IAAA;AAAA,EACV,QAAA,GAAW,OAAA;AAAA,EACX,OAAA;AAAA,EACA;AACF,CAAA,EAQ+B;AAC7B,EAAA,MAAM,aAAa,mBAAA,CAAoB;AAAA,IACrC,WAAA;AAAA,IACA,QAAA;AAAA,IACA,WAAA;AAAA,IACA,OAAA;AAAA,IACA;AAAA,GACD,CAAA;AACD,EAAA,MAAM,WAAW,mBAAA,CAAoB;AAAA,IACnC,QAAA;AAAA,IACA,aAAa,UAAA,CAAW,WAAA;AAAA,IACxB;AAAA,GACD,CAAA;AACD,EAAA,MAAM,aAAA,GAAgB,OAAA,IAAW,OAAA,CAAQ,QAAA,CAAS,GAAG,CAAA;AACrD,EAAA,MAAM,SAAA,GAAY,OAAA,GAAU,QAAA,CAAS,SAAA,GAAY,UAAA,CAAW,SAAA;AAC5D,EAAA,MAAM,YAAY,sBAAA,CAAuB;AAAA,IACvC,OAAA,EAAS,aAAA;AAAA,IACT,OAAA;AAAA,IACA;AAAA,GACD,CAAA;AAED,EAAA,OAAO,OAAA;AAAA,IACL,OAAO;AAAA,MACL,aAAa,UAAA,CAAW,WAAA;AAAA,MACxB,aAAa,UAAA,CAAW,WAAA;AAAA,MACxB,SAAA;AAAA,MACA,SAAA;AAAA,MACA,KAAA,EAAO;AAAA,QACL,aAAa,UAAA,CAAW,WAAA;AAAA,QACxB,QAAA,EAAU,UAAU,QAAA,GAAW,MAAA;AAAA,QAC/B,GAAA,EAAK,OAAA,GAAU,QAAA,CAAS,GAAA,GAAM,MAAA;AAAA,QAC9B,QAAQ,QAAA,CAAS,MAAA;AAAA,QACjB,GAAG,SAAA,CAAU;AAAA,OACf;AAAA,MACA,OAAA,EAAS;AAAA,KACX,CAAA;AAAA,IACA,CAAC,OAAA,EAAS,SAAA,EAAW,UAAU,SAAA,EAAW,aAAA,EAAe,UAAU,UAAU;AAAA,GAC/E;AACF;AAQO,SAAS,sBAAA,CAAuB;AAAA,EACrC,OAAA,GAAU,IAAA;AAAA,EACV,aAAA;AAAA,EACA,OAAA;AAAA,EACA;AACF,CAAA,GAKI,EAAC,EAAwB;AAC3B,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAI,QAAA;AAAA,IAC1B,aAAA,KAAkB,UAAU,SAAA,GAAY,MAAA;AAAA,GAC1C;AACA,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,SAAuB,IAAI,CAAA;AAErD,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,SAAA,CAAU,OAAA,GAAU,aAAA,IAAiB,SAAA,GAAY,MAAM,CAAA;AACvD,IAAA,QAAA,CAAS,IAAI,CAAA;AAAA,EACf,CAAA,EAAG,CAAC,OAAA,EAAS,aAAa,CAAC,CAAA;AAE3B,EAAA,MAAM,kBAAA,GAAqB,WAAA;AAAA,IACzB,CAAC,UAAA,KAAiC;AAChC,MAAA,SAAA,CAAU,UAAU,CAAA;AACpB,MAAA,IAAI,eAAe,OAAA,EAAS;AAC1B,QAAA,QAAA,CAAS,IAAI,CAAA;AAAA,MACf;AACA,MAAA,cAAA,GAAiB,UAAU,CAAA;AAAA,IAC7B,CAAA;AAAA,IACA,CAAC,cAAc;AAAA,GACjB;AAEA,EAAA,MAAM,WAAA,GAAc,WAAA;AAAA,IAClB,CAAC,SAAA,KAAqB;AACpB,MAAA,QAAA,CAAS,SAAS,CAAA;AAClB,MAAA,SAAA,CAAU,OAAO,CAAA;AACjB,MAAA,OAAA,GAAU,SAAS,CAAA;AAAA,IACrB,CAAA;AAAA,IACA,CAAC,OAAO;AAAA,GACV;AAEA,EAAA,MAAM,KAAA,GAAQ,YAAY,MAAM;AAC9B,IAAA,SAAA,CAAU,OAAA,GAAU,aAAA,IAAiB,SAAA,GAAY,MAAM,CAAA;AACvD,IAAA,QAAA,CAAS,IAAI,CAAA;AAAA,EACf,CAAA,EAAG,CAAC,OAAA,EAAS,aAAa,CAAC,CAAA;AAE3B,EAAA,OAAO,OAAA;AAAA,IACL,OAAO;AAAA,MACL,MAAA;AAAA,MACA,KAAA;AAAA,MACA,WAAW,MAAA,KAAW,SAAA;AAAA,MACtB,SAAS,MAAA,KAAW,OAAA;AAAA,MACpB,SAAS,MAAA,KAAW,OAAA;AAAA,MACpB,KAAA,EAAO;AAAA,QACL,cAAA,EAAgB,kBAAA;AAAA,QAChB,OAAA,EAAS;AAAA,OACX;AAAA,MACA;AAAA,KACF,CAAA;AAAA,IACA,CAAC,KAAA,EAAO,WAAA,EAAa,kBAAA,EAAoB,OAAO,MAAM;AAAA,GACxD;AACF;AAQO,SAAS,qBAAA,CAAsB;AAAA,EACpC,WAAA;AAAA,EACA,QAAA;AAAA,EACA,QAAA,GAAW,OAAA;AAAA,EACX,GAAA;AAAA,EACA,MAAA;AAAA,EACA,cAAA;AAAA,EACA,sBAAA;AAAA,EACA,eAAA;AAAA,EACA,QAAA;AAAA,EACA,GAAA,GAAM,IAAA;AAAA,EACN,YAAA;AAAA,EACA,aAAA;AAAA,EACA,gBAAA,GAAmB,KAAA;AAAA,EACnB,cAAA;AAAA,EACA,MAAA;AAAA,EACA,OAAA;AAAA,EACA,GAAG;AACL,CAAA,EAA+B;AAC7B,EAAA,MAAM,QAAA,GAAW,OAAoB,IAAI,CAAA;AACzC,EAAA,MAAM,QAAA,GAAW,OAAqC,IAAI,CAAA;AAC1D,EAAA,MAAM,eAAA,GAAkB,OAAoC,IAAI,CAAA;AAChE,EAAA,MAAM,OAAA,GAAU,OAAsC,IAAI,CAAA;AAC1D,EAAA,MAAM,eAAA,GAAkB,MAAA,CAAqB,EAAE,CAAA;AAC/C,EAAA,MAAM,iBAAA,GAAoB,OAAO,cAAc,CAAA;AAC/C,EAAA,MAAM,SAAA,GAAY,OAAO,MAAM,CAAA;AAC/B,EAAA,MAAM,UAAA,GAAa,OAAO,OAAO,CAAA;AACjC,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAI,SAA2B,MAAM,CAAA;AAC7D,EAAA,MAAM,EAAE,EAAA,EAAI,UAAA,EAAW,GAAI,QAAA,EAAS;AACpC,EAAA,iBAAA,CAAkB,OAAA,GAAU,cAAA;AAC5B,EAAA,SAAA,CAAU,OAAA,GAAU,MAAA;AACpB,EAAA,UAAA,CAAW,OAAA,GAAU,OAAA;AAErB,EAAA,MAAM,WAAW,mBAAA,CAAoB;AAAA,IACnC,WAAA;AAAA,IACA,QAAA;AAAA,IACA,QAAA;AAAA,IACA,GAAA;AAAA,IACA,MAAA;AAAA,IACA,cAAA,EAAgB;AAAA,GACjB,CAAA;AACD,EAAA,MAAM,oBAAA,GAAuB,OAAA;AAAA,IAC3B,MAAM,mBAAA,CAAoB,0BAAA,CAA2B,GAAG,GAAG,YAAY,CAAA;AAAA,IACvE;AAAA,MACE,GAAA;AAAA,MACA,YAAA,EAAc,cAAA;AAAA,MACd,YAAA,EAAc,aAAA;AAAA,MACd,YAAA,EAAc;AAAA;AAChB,GACF;AACA,EAAA,MAAM,qBAAA,GAAwB,OAAA;AAAA,IAC5B,MAAM,oBAAA,CAAqB,GAAA,EAAK,aAAa,CAAA;AAAA,IAC7C;AAAA,MACE,aAAA,EAAe,mBAAA;AAAA,MACf,aAAA,EAAe,iBAAA;AAAA,MACf,aAAA,EAAe,gBAAA;AAAA,MACf,aAAA,EAAe,iBAAA;AAAA,MACf,aAAA,EAAe,cAAA;AAAA,MACf,aAAA,EAAe,aAAA;AAAA,MACf,aAAA,EAAe,eAAA;AAAA,MACf,aAAA,EAAe,iBAAA;AAAA,MACf;AAAA;AACF,GACF;AAEA,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,QAAA,GAAW,KAAA;AACf,IAAA,kCAAA,EAAmC;AAEnC,IAAA,SAAS,mBAAmB,UAAA,EAA8B;AACxD,MAAA,SAAA,CAAU,UAAU,CAAA;AACpB,MAAA,iBAAA,CAAkB,UAAU,UAAU,CAAA;AAAA,IACxC;AAEA,IAAA,SAAS,mBAAA,GAAsB;AAC7B,MAAA,KAAA,MAAW,IAAA,IAAQ,gBAAgB,OAAA,EAAS;AAC1C,QAAA,IAAA,CAAK,OAAA,GAAU,IAAA;AAAA,MACjB;AACA,MAAA,eAAA,CAAgB,UAAU,EAAC;AAAA,IAC7B;AAEA,IAAA,SAAS,mBAAA,GAAsB;AAC7B,MAAA,0BAAA,CAA2B,eAAA,CAAgB,SAAS,QAAQ,CAAA;AAC5D,MAAA,eAAA,CAAgB,OAAA,GAAU,IAAA;AAAA,IAC5B;AAEA,IAAA,eAAe,SAAA,GAAY;AACzB,MAAA,IAAI,CAAC,SAAS,GAAA,EAAK;AACjB,QAAA,kBAAA,CAAmB,MAAM,CAAA;AACzB,QAAA;AAAA,MACF;AAEA,MAAA,IAAI,QAAA,CAAS,WAAW,KAAA,EAAO;AAC7B,QAAA,MAAM,yBAAyB,IAAI,KAAA;AAAA,UACjC,CAAA,2DAAA,EAA8D,SAAS,MAAM,CAAA,EAAA;AAAA,SAC/E;AACA,QAAA,kBAAA,CAAmB,OAAO,CAAA;AAC1B,QAAA,UAAA,CAAW,UAAU,sBAAsB,CAAA;AAC3C,QAAA;AAAA,MACF;AAEA,MAAA,kBAAA,CAAmB,SAAS,CAAA;AAE5B,MAAA,IAAI;AACF,QAAA,MAAM,WAAA,GAAc,MAAM,OAAO,mBAAmB,CAAA;AACpD,QAAA,IAAI,QAAA,IAAY,CAAC,QAAA,CAAS,OAAA,EAAS;AAEnC,QAAA,MAAM,KAAA,GAAQ,IAAI,WAAA,CAAY,aAAA,CAAc;AAAA,UAC1C,QAAA,EAAU,EAAA;AAAA,UACV,OAAA,EAAS,UAAA;AAAA,UACT,GAAG;AAAA,SACJ,CAAA;AACD,QAAA,KAAA,CAAM,QAAA,CAAS,yCAAyC,CAAA,GAAI,OAAO;AAAA,UACjE,KAAA;AAAA,UACA,MAAA;AAAA,UACA,KAAA;AAAA,UACA;AAAA,SACF,KAA8E;AAC5E,UAAA,IAAI,eAAe,eAAA,CAAgB,OAAA;AACnC,UAAA,IACE,CAAC,YAAA,IACD,YAAA,CAAa,UAAU,KAAA,IACvB,YAAA,CAAa,WAAW,MAAA,EACxB;AACA,YAAA,mBAAA,EAAoB;AACpB,YAAA,YAAA,GAAe;AAAA,cACb,QAAA,EAAU,IAAI,WAAA,CAAY,aAAA,CAAc;AAAA,gBACtC,QAAA,EAAU,EAAA;AAAA,gBACV,UAAA,EAAY,KAAA;AAAA,gBACZ,eAAe,qBAAA,CAAsB,aAAA;AAAA,gBACrC,gBAAgB,qBAAA,CAAsB,cAAA;AAAA,gBACtC,mBAAmB,qBAAA,CAAsB,iBAAA;AAAA,gBACzC,MAAA,EAAQ;AAAA,kBACN,KAAA;AAAA,kBACA;AAAA;AACF,eACD,CAAA;AAAA,cACD,KAAA;AAAA,cACA;AAAA,aACF;AACA,YAAA,eAAA,CAAgB,OAAA,GAAU,YAAA;AAAA,UAC5B;AAEA,UAAA,MAAM,kBAAkB,YAAA,CAAa,QAAA;AACrC,UAAA,MAAM,kBAAkB,IAAA,CAAK,GAAA;AAAA,YAC3B,CAAA;AAAA,YACA,IAAA,CAAK,KAAA,CAAM,qBAAA,CAAsB,eAAe;AAAA,WAClD;AACA,UAAA,IAAI,MAAA;AACJ,UAAA,KAAA,IAAS,OAAA,GAAU,CAAA,EAAG,OAAA,GAAU,eAAA,EAAiB,WAAW,CAAA,EAAG;AAC7D,YAAA,eAAA,CAAgB,UAAA,CAAW,GAAA,CAAI,KAAA,EAAO,MAAM,CAAA;AAC5C,YAAA,MAAM,eAAA,CAAgB,MAAA,CAAO,EAAE,KAAA,EAAO,QAAQ,CAAA;AAC9C,YAAA,MAAA,GAAS,MAAM,gBAAgB,gBAAA,CAAiB;AAAA,cAC9C,KAAA;AAAA,cACA;AAAA,aACD,CAAA;AACD,YAAA,IACE,gBAAgB,YAAA,GAAe,CAAA,IAC/B,CAAC,oBAAA,CAAqB,MAAA,EAAQ,qBAAqB,CAAA,EACnD;AACA,cAAA;AAAA,YACF;AACA,YAAA,IAAI,OAAA,GAAU,kBAAkB,CAAA,EAAG;AACjC,cAAA,MAAM,yBAAA,EAA0B;AAAA,YAClC;AAAA,UACF;AACA,UAAA,IAAI,CAAC,MAAA,EAAQ;AACX,YAAA,MAAM,IAAI,MAAM,oDAAoD,CAAA;AAAA,UACtE;AACA,UAAA,OAAO,EAAE,MAAA,EAAQ,KAAA,EAAO,MAAA,EAAQ,OAAO,IAAA,EAAK;AAAA,QAC9C,CAAA;AACA,QAAA,MAAM,IAAA,GAAO,IAAI,WAAA,CAAY,SAAA,CAAU;AAAA,UACrC,KAAK,QAAA,CAAS,GAAA;AAAA,UACd;AAAA,SACD,CAAA;AACD,QAAA,IAAA,CAAK,IAAA,GAAO,mBAAA;AAEZ,QAAA,QAAA,CAAS,OAAA,CAAQ,IAAI,KAAK,CAAA;AAC1B,QAAA,QAAA,CAAS,OAAA,CAAQ,IAAI,IAAI,CAAA;AACzB,QAAA,QAAA,CAAS,OAAA,GAAU,KAAA;AACnB,QAAA,OAAA,CAAQ,OAAA,GAAU,IAAA;AAElB,QAAA,IAAI,gBAAA,IAAoB,QAAA,CAAS,OAAA,CAAQ,MAAA,EAAQ;AAC/C,UAAA,QAAA,CAAS,OAAA,CAAQ,MAAA,CAAO,QAAA,CAAS,CAAC,MAAA,KAAW;AAC3C,YAAA,IACE,EAAE,MAAA,YAAwB,KAAA,CAAA,IAAA,CAAA,IAC1B,MAAA,KAAY,IAAA,EACZ;AACA,cAAA;AAAA,YACF;AACA,YAAA,MAAM,IAAA,GAAO,MAAA,CAAO,IAAA,CAAK,WAAA,EAAY;AACrC,YAAA,IACE,IAAA,CAAK,QAAA,CAAS,OAAO,CAAA,IACrB,IAAA,CAAK,QAAA,CAAS,QAAQ,CAAA,IACtB,IAAA,CAAK,QAAA,CAAS,OAAO,CAAA,EACrB;AACA,cAAA,MAAA,CAAO,OAAA,GAAU,KAAA;AACjB,cAAA,eAAA,CAAgB,OAAA,CAAQ,KAAK,MAAM,CAAA;AAAA,YACrC;AAAA,UACF,CAAC,CAAA;AAAA,QACH;AAEA,QAAA,MAAM,IAAA,CAAK,WAAA;AACX,QAAA,IAAI,QAAA,EAAU;AACd,QAAA,kBAAA,CAAmB,OAAO,CAAA;AAC1B,QAAA,SAAA,CAAU,UAAU,IAAI,CAAA;AACxB,QAAA,UAAA,EAAW;AAAA,MACb,SAAS,KAAA,EAAO;AACd,QAAA,MAAM,eAAA,GACJ,iBAAiB,KAAA,GAAQ,KAAA,GAAQ,IAAI,KAAA,CAAM,MAAA,CAAO,KAAK,CAAC,CAAA;AAC1D,QAAA,kBAAA,CAAmB,OAAO,CAAA;AAC1B,QAAA,UAAA,CAAW,UAAU,eAAe,CAAA;AAAA,MACtC;AAAA,IACF;AAEA,IAAA,KAAK,SAAA,EAAU;AAEf,IAAA,OAAO,MAAM;AACX,MAAA,QAAA,GAAW,IAAA;AACX,MAAA,mBAAA,EAAoB;AAEpB,MAAA,IAAI,QAAQ,OAAA,EAAS;AACnB,QAAA,QAAA,CAAS,OAAA,EAAS,MAAA,CAAO,OAAA,CAAQ,OAAO,CAAA;AACxC,QAAA,0BAAA,CAA2B,QAAQ,OAAO,CAAA;AAC1C,QAAA,OAAA,CAAQ,OAAA,GAAU,IAAA;AAAA,MACpB;AAEA,MAAA,IAAI,SAAS,OAAA,EAAS;AACpB,QAAA,QAAA,CAAS,OAAA,EAAS,MAAA,CAAO,QAAA,CAAS,OAAO,CAAA;AACzC,QAAA,0BAAA,CAA2B,SAAS,OAAO,CAAA;AAC3C,QAAA,QAAA,CAAS,OAAA,GAAU,IAAA;AAAA,MACrB;AAEA,MAAA,mBAAA,EAAoB;AAAA,IACtB,CAAA;AAAA,EACF,CAAA,EAAG;AAAA,IACD,EAAA;AAAA,IACA,gBAAA;AAAA,IACA,UAAA;AAAA,IACA,GAAA;AAAA,IACA,QAAA,CAAS,MAAA;AAAA,IACT,QAAA,CAAS,GAAA;AAAA,IACT,qBAAA,CAAsB,mBAAA;AAAA,IACtB,qBAAA,CAAsB,iBAAA;AAAA,IACtB,qBAAA,CAAsB,gBAAA;AAAA,IACtB,qBAAA,CAAsB,iBAAA;AAAA,IACtB,qBAAA,CAAsB,cAAA;AAAA,IACtB,qBAAA,CAAsB,aAAA;AAAA,IACtB,qBAAA,CAAsB,eAAA;AAAA,IACtB,qBAAA,CAAsB,iBAAA;AAAA,IACtB,oBAAA,CAAqB,cAAA;AAAA,IACrB,oBAAA,CAAqB,aAAA;AAAA,IACrB,oBAAA,CAAqB;AAAA,GACtB,CAAA;AAED,EAAA,uBACE,IAAA;AAAA,IAAC,gBAAA;AAAA,IAAA;AAAA,MACE,GAAG,UAAA;AAAA,MACJ,WAAA;AAAA,MACA,QAAA;AAAA,MACA,QAAA;AAAA,MACA,KAAK,QAAA,CAAS,GAAA;AAAA,MACd,QAAQ,QAAA,CAAS,MAAA;AAAA,MACjB,wBAAwB,QAAA,CAAS,cAAA;AAAA,MACjC,cAAA;AAAA,MACA,eAAA,EAAiB,mBAAmB,MAAA,KAAW,OAAA;AAAA,MAE/C,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,OAAA,EAAA,EAAM,KAAK,QAAA,EAAU,CAAA;AAAA,QACrB;AAAA;AAAA;AAAA,GACH;AAEJ;AAEA,SAAS,2BACP,QAAA,EACA;AACA,EAAA,IAAI,CAAC,QAAA,EAAU;AACf,EAAA,IAAI;AACF,IAAA,qCAAA,CAAsC,QAAQ,CAAA;AAC9C,IAAA,MAAM,MAAA,GAAS,SAAS,OAAA,IAAU;AAClC,IAAA,IAAI,aAAA,CAAc,MAAM,CAAA,EAAG;AACzB,MAAA,KAAK,OAAA,CAAQ,OAAA,CAAQ,MAAM,CAAA,CAAE,MAAM,uBAAuB,CAAA;AAAA,IAC5D;AAAA,EACF,SAAS,KAAA,EAAO;AACd,IAAA,uBAAA,CAAwB,KAAK,CAAA;AAAA,EAC/B;AACF;AAEA,SAAS,cAAc,KAAA,EAA+C;AACpE,EAAA,OACE,OAAO,UAAU,QAAA,IACjB,KAAA,KAAU,QACV,MAAA,IAAU,KAAA,IACV,OAAQ,KAAA,CAA6B,IAAA,KAAS,UAAA;AAElD;AAEA,SAAS,sCAAsC,QAAA,EAA2B;AACxE,EAAA,MAAM,OAAA,GAAU,gBAAgB,QAAQ,CAAA;AACxC,EAAA,KAAA,MAAW,UAAU,OAAA,EAAS;AAC5B,IAAA,IAAI,CAAC,OAAO,QAAA,EAAU;AAEtB,IAAA,KAAA,MAAW,OAAA,IAAW,MAAA,CAAO,MAAA,CAAO,MAAA,CAAO,QAAQ,CAAA,EAAG;AACpD,MAAA,MAAM,SAAS,OAAA,CAAQ,MAAA;AACvB,MAAA,IAAI,CAAC,MAAA,EAAQ;AAEb,MAAA,OAAA,CAAQ,MAAA,GAAS,CAAC,KAAA,KAAmB;AACnC,QAAA,IAAI,CAAC,2BAAA,CAA4B,KAAK,CAAA,EAAG;AACvC,UAAA,MAAA,CAAO,KAAK,CAAA;AAAA,QACd;AAAA,MACF,CAAA;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,gBAAgB,QAAA,EAA8C;AACrE,EAAA,MAAM,aAAA,GAAgB,QAAA;AACtB,EAAA,OAAO;AAAA,IACL,aAAA,CAAc,MAAA;AAAA,IACd,aAAA,CAAc,UAAA;AAAA,IACd,aAAA,CAAc;AAAA,IACd,MAAA,CAAO,CAAC,MAAA,KAAsC,OAAA,CAAQ,MAAM,CAAC,CAAA;AACjE;AAEA,SAAS,wBAAwB,KAAA,EAAgB;AAC/C,EAAA,IACE,KAAA,YAAiB,SACjB,KAAA,CAAM,OAAA,CAAQ,aAAY,CAAE,QAAA,CAAS,kBAAkB,CAAA,EACvD;AACA,IAAA;AAAA,EACF;AAEA,EAAA,OAAA,CAAQ,IAAA,CAAK,kDAAkD,KAAK,CAAA;AACtE;AAEA,SAAS,kCAAA,GAAqC;AAC5C,EAAA,IACE,0CACA,OAAO,MAAA,KAAW,eAClB,OAAO,MAAA,CAAO,qBAAqB,UAAA,EACnC;AACA,IAAA;AAAA,EACF;AAEA,EAAA,sCAAA,GAAyC,IAAA;AACzC,EAAA,MAAA,CAAO,gBAAA,CAAiB,oBAAA,EAAsB,CAAC,KAAA,KAAU;AACvD,IAAA,IAAI,2BAAA,CAA4B,KAAA,CAAM,MAAM,CAAA,EAAG;AAC7C,MAAA,KAAA,CAAM,cAAA,EAAe;AAAA,IACvB;AAAA,EACF,CAAC,CAAA;AACH;AAEA,SAAS,4BAA4B,MAAA,EAAiB;AACpD,EAAA,OACE,kBAAkB,KAAA,IAClB,MAAA,CAAO,QAAQ,WAAA,EAAY,CAAE,SAAS,kBAAkB,CAAA;AAE5D","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 onStatusChangeRef.current = onStatusChange;\n onLoadRef.current = onLoad;\n onErrorRef.current = onError;\n\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 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"]}
|
|
@@ -223,6 +223,9 @@ interface MujocoModel {
|
|
|
223
223
|
cam_pos?: Float64Array;
|
|
224
224
|
cam_quat?: Float64Array;
|
|
225
225
|
cam_fovy?: Float64Array;
|
|
226
|
+
cam_intrinsic?: Float64Array;
|
|
227
|
+
cam_resolution?: Int32Array;
|
|
228
|
+
cam_sensorsize?: Float64Array;
|
|
226
229
|
ten_wrapadr: Int32Array;
|
|
227
230
|
ten_wrapnum: Int32Array;
|
|
228
231
|
ten_range: Float64Array;
|
|
@@ -556,6 +559,9 @@ interface CameraInfo {
|
|
|
556
559
|
name: string;
|
|
557
560
|
bodyId: number;
|
|
558
561
|
fov: number | null;
|
|
562
|
+
resolution: [number, number] | null;
|
|
563
|
+
sensorSize: [number, number] | null;
|
|
564
|
+
intrinsic: [number, number, number, number] | null;
|
|
559
565
|
position: [number, number, number] | null;
|
|
560
566
|
quaternion: [number, number, number, number] | null;
|
|
561
567
|
}
|
|
@@ -795,11 +801,27 @@ interface ObservationHandle {
|
|
|
795
801
|
/** Read just the vector values for policy inference. */
|
|
796
802
|
readValues(): Float32Array | Float64Array;
|
|
797
803
|
}
|
|
804
|
+
interface DebugVirtualCamera {
|
|
805
|
+
name?: string;
|
|
806
|
+
position?: CameraFrameCaptureVector3;
|
|
807
|
+
lookAt?: CameraFrameCaptureVector3;
|
|
808
|
+
up?: CameraFrameCaptureVector3;
|
|
809
|
+
quaternion?: THREE.Quaternion | readonly [number, number, number, number];
|
|
810
|
+
fov?: number;
|
|
811
|
+
width?: number;
|
|
812
|
+
height?: number;
|
|
813
|
+
frustumDepth?: number;
|
|
814
|
+
markerScale?: number;
|
|
815
|
+
color?: THREE.ColorRepresentation;
|
|
816
|
+
aimColor?: THREE.ColorRepresentation;
|
|
817
|
+
}
|
|
798
818
|
interface DebugProps {
|
|
799
819
|
showGeoms?: boolean;
|
|
800
820
|
showSites?: boolean;
|
|
801
821
|
showJoints?: boolean;
|
|
802
822
|
showCameras?: boolean;
|
|
823
|
+
/** Additional explicit virtual camera poses to visualize alongside MuJoCo XML cameras. */
|
|
824
|
+
virtualCameras?: readonly DebugVirtualCamera[];
|
|
803
825
|
showContacts?: boolean;
|
|
804
826
|
showCOM?: boolean;
|
|
805
827
|
showInertia?: boolean;
|
|
@@ -1162,6 +1184,49 @@ interface FrameCaptureAPI {
|
|
|
1162
1184
|
}
|
|
1163
1185
|
type CameraFrameCaptureVector3 = THREE.Vector3 | readonly [number, number, number];
|
|
1164
1186
|
type CameraFrameCaptureQuaternion = THREE.Quaternion | readonly [number, number, number, number];
|
|
1187
|
+
interface CameraFrameVisualOverrides {
|
|
1188
|
+
/**
|
|
1189
|
+
* Override `scene.background` for this capture only.
|
|
1190
|
+
* Use `null` or `false` to render without the viewer scene background.
|
|
1191
|
+
*/
|
|
1192
|
+
sceneBackground?: THREE.Scene['background'] | THREE.ColorRepresentation | null | false;
|
|
1193
|
+
/**
|
|
1194
|
+
* Override `scene.environment` for this capture only.
|
|
1195
|
+
* Use `null` or `false` to remove viewer environment lighting/maps.
|
|
1196
|
+
*/
|
|
1197
|
+
sceneEnvironment?: THREE.Scene['environment'] | null | false;
|
|
1198
|
+
/**
|
|
1199
|
+
* Override `scene.fog` for this capture only.
|
|
1200
|
+
* Use `null` or `false` to remove viewer fog.
|
|
1201
|
+
*/
|
|
1202
|
+
sceneFog?: THREE.Scene['fog'] | null | false;
|
|
1203
|
+
/** Override `renderer.shadowMap.enabled` while capturing. */
|
|
1204
|
+
shadows?: boolean;
|
|
1205
|
+
/** Override renderer tone mapping while capturing. */
|
|
1206
|
+
toneMapping?: THREE.WebGLRenderer['toneMapping'];
|
|
1207
|
+
/** Override renderer output color space while capturing. */
|
|
1208
|
+
outputColorSpace?: THREE.WebGLRenderer['outputColorSpace'];
|
|
1209
|
+
}
|
|
1210
|
+
interface CameraFrameRenderIsolationOptions {
|
|
1211
|
+
/**
|
|
1212
|
+
* Use an independent offscreen WebGLRenderer for this capture.
|
|
1213
|
+
*
|
|
1214
|
+
* This prevents viewer renderer settings such as antialiasing, shadow-map
|
|
1215
|
+
* configuration, tone mapping, and environment setup from leaking into
|
|
1216
|
+
* policy/training images. Leave unset for the historical shared-renderer path.
|
|
1217
|
+
*/
|
|
1218
|
+
enabled?: boolean;
|
|
1219
|
+
/** Offscreen renderer antialiasing. Defaults to false for deterministic policy captures. */
|
|
1220
|
+
antialias?: boolean;
|
|
1221
|
+
/** Offscreen renderer alpha buffer. Defaults to false, matching Three.js WebGLRenderer. */
|
|
1222
|
+
alpha?: boolean;
|
|
1223
|
+
/** Offscreen renderer preserveDrawingBuffer flag. Defaults to false. */
|
|
1224
|
+
preserveDrawingBuffer?: boolean;
|
|
1225
|
+
/** Offscreen renderer power preference. Defaults to the browser's renderer default. */
|
|
1226
|
+
powerPreference?: WebGLPowerPreference;
|
|
1227
|
+
/** Reuse an offscreen renderer for matching capture dimensions/options. Defaults to true. */
|
|
1228
|
+
cache?: boolean;
|
|
1229
|
+
}
|
|
1165
1230
|
interface CameraFrameCaptureOptions {
|
|
1166
1231
|
/** Existing Three camera to clone before applying pose overrides. */
|
|
1167
1232
|
camera?: THREE.Camera;
|
|
@@ -1186,8 +1251,36 @@ interface CameraFrameCaptureOptions {
|
|
|
1186
1251
|
fov?: number;
|
|
1187
1252
|
near?: number;
|
|
1188
1253
|
far?: number;
|
|
1254
|
+
/**
|
|
1255
|
+
* Explicit projection matrix for offscreen capture. This is useful when a
|
|
1256
|
+
* MuJoCo camera has calibrated intrinsics that cannot be represented by a
|
|
1257
|
+
* symmetric Three.js PerspectiveCamera fov alone.
|
|
1258
|
+
*/
|
|
1259
|
+
projectionMatrix?: THREE.Matrix4 | readonly number[];
|
|
1189
1260
|
/** Provenance for the camera pose used by the capture. Usually set by the MuJoCo provider. */
|
|
1190
1261
|
source?: CameraFrameCaptureSource;
|
|
1262
|
+
/**
|
|
1263
|
+
* When resolving a named MuJoCo camera, derive Three capture settings from
|
|
1264
|
+
* MuJoCo camera metadata where available: cam_resolution, cam_fovy,
|
|
1265
|
+
* cam_intrinsic/cam_sensorsize, and visual map near/far clipping.
|
|
1266
|
+
*/
|
|
1267
|
+
mujocoCameraCompatibility?: boolean | {
|
|
1268
|
+
useResolution?: boolean;
|
|
1269
|
+
useIntrinsics?: boolean;
|
|
1270
|
+
useClipping?: boolean;
|
|
1271
|
+
/**
|
|
1272
|
+
* When a MuJoCo camera has `resolution` metadata and the caller supplies
|
|
1273
|
+
* only width or only height, derive the missing dimension from the MuJoCo
|
|
1274
|
+
* camera aspect ratio.
|
|
1275
|
+
*/
|
|
1276
|
+
preserveAspect?: boolean;
|
|
1277
|
+
/**
|
|
1278
|
+
* Prefer the MuJoCo camera's configured resolution over width/height
|
|
1279
|
+
* provided by the caller. Leave false when a policy wants fixed-size
|
|
1280
|
+
* payloads while still preserving the camera aspect ratio.
|
|
1281
|
+
*/
|
|
1282
|
+
preferResolution?: boolean;
|
|
1283
|
+
};
|
|
1191
1284
|
/** Hide rendered Three objects whose MuJoCo geom group is in this list. */
|
|
1192
1285
|
hiddenGeomGroups?: readonly number[];
|
|
1193
1286
|
/** When provided, only rendered Three objects whose MuJoCo geom group is in this list are visible. */
|
|
@@ -1198,6 +1291,15 @@ interface CameraFrameCaptureOptions {
|
|
|
1198
1291
|
background?: THREE.ColorRepresentation;
|
|
1199
1292
|
/** Optional clear alpha for this capture only. Defaults to the renderer's current clear alpha. */
|
|
1200
1293
|
backgroundAlpha?: number;
|
|
1294
|
+
/** Temporary scene/renderer visual overrides applied only for this offscreen capture. */
|
|
1295
|
+
visualOverrides?: CameraFrameVisualOverrides;
|
|
1296
|
+
/**
|
|
1297
|
+
* Render this capture with a separate offscreen WebGLRenderer.
|
|
1298
|
+
*
|
|
1299
|
+
* This is useful for policy or training captures that should remain canonical
|
|
1300
|
+
* while the interactive viewer uses richer visual effects.
|
|
1301
|
+
*/
|
|
1302
|
+
renderIsolation?: boolean | CameraFrameRenderIsolationOptions;
|
|
1201
1303
|
/** Mirror the captured image horizontally after rendering. Useful when matching policy datasets with mirrored camera frames. */
|
|
1202
1304
|
flipX?: boolean;
|
|
1203
1305
|
}
|
|
@@ -1307,6 +1409,17 @@ interface CameraFrameSequenceRecorderAPI {
|
|
|
1307
1409
|
record: (options: CameraFrameSequenceOptions) => Promise<CameraFrameSequenceResult>;
|
|
1308
1410
|
reset: () => void;
|
|
1309
1411
|
}
|
|
1412
|
+
type MujocoMeshNormalSmoothing = boolean | {
|
|
1413
|
+
/** Vertex merge tolerance used before recomputing mesh normals. Defaults to `1e-4`. */
|
|
1414
|
+
tolerance?: number;
|
|
1415
|
+
};
|
|
1416
|
+
interface MujocoRenderOptions {
|
|
1417
|
+
/**
|
|
1418
|
+
* Smooth mesh normals by welding duplicate vertices before recomputing normals.
|
|
1419
|
+
* Useful for faceted STL visuals; keep off for exact policy-render parity.
|
|
1420
|
+
*/
|
|
1421
|
+
meshNormalSmoothing?: MujocoMeshNormalSmoothing;
|
|
1422
|
+
}
|
|
1310
1423
|
type MujocoCanvasProps = Omit<CanvasProps, 'onError'> & {
|
|
1311
1424
|
config: SceneConfig;
|
|
1312
1425
|
/** R3F content rendered while the MuJoCo WASM module is still loading. */
|
|
@@ -1321,6 +1434,7 @@ type MujocoCanvasProps = Omit<CanvasProps, 'onError'> & {
|
|
|
1321
1434
|
paused?: boolean;
|
|
1322
1435
|
speed?: number;
|
|
1323
1436
|
interpolate?: boolean;
|
|
1437
|
+
renderOptions?: MujocoRenderOptions;
|
|
1324
1438
|
};
|
|
1325
1439
|
interface SitePositionResult {
|
|
1326
1440
|
position: React__default.RefObject<THREE.Vector3>;
|
|
@@ -1384,4 +1498,4 @@ interface ArrayJointStateResult {
|
|
|
1384
1498
|
velocity: React__default.RefObject<Float64Array>;
|
|
1385
1499
|
}
|
|
1386
1500
|
|
|
1387
|
-
export { type
|
|
1501
|
+
export { type ScalarJointStateResult as $, type ActuatedJointInfo as A, type BodyProps as B, type ControlGroupInfo as C, type DragInteractionProps as D, type VisualScenarioExecutionContext as E, type ScenarioLightingPreset as F, type SplatEnvironmentMetadataInput as G, type SplatEnvironmentMetadata as H, type IkConfig as I, type SplatSceneInput as J, type DebugProps as K, type GeomInfo as L, type MujocoContextValue as M, type ContactListenerProps as N, type ObservationConfig as O, type PhysicsStepCallback as P, type ActuatorInfo as Q, type ReadyCallbackInput as R, type SceneConfig as S, type TrajectoryPlayerProps as T, type Sites as U, type VisualScenarioEffectsProps as V, type SitePositionResult as W, type Sensors as X, type SensorHandle as Y, type SensorInfo as Z, type Joints as _, type MujocoCanvasProps as a, type KeyboardIkTargetBinding as a$, type ArrayJointStateResult as a0, type JointStateOptions as a1, type JointStateResult as a2, type Bodies as a3, type BodyStateResult as a4, type Geoms as a5, type Actuators as a6, type CtrlHandle as a7, type ContactInfo as a8, type KeyboardTeleopConfig as a9, type PolicyCameraFrameCaptureAPI as aA, type CameraFrameSequenceRecorderAPI as aB, type CameraFrameCaptureResult as aC, type CameraFrameCaptureBlobResult as aD, type ImagePointCoordinateSpace as aE, type ImagePointProjectionOptions as aF, type ImagePointProjectionResult as aG, type PolicyVector as aH, type BodyInfo as aI, type CameraFrameCaptureQuaternion as aJ, type CameraFrameCaptureVector3 as aK, type CameraFrameSequenceCameraSummary as aL, type CameraFrameSequenceFrame as aM, type CameraFrameSequenceSampleInput as aN, type CameraFrameSequenceStepInput as aO, type CameraInfo as aP, type ControlJointInfo as aQ, type DebugVirtualCamera as aR, type FrameCaptureTarget as aS, type FrameCaptureTargetRef as aT, type IKSolveFn as aU, type IkGizmoDragInput as aV, type IkSolveInput as aW, type JointInfo as aX, type JointStateKind as aY, type KeyBinding as aZ, type KeyboardIkTargetAction as a_, type KeyboardIkTargetConfig as aa, type PolicyConfig as ab, type PolicyAPI as ac, type RemotePolicyConfig as ad, type RemotePolicyAPI as ae, type ObservationHandle as af, type ObservationOutput as ag, type TrajectoryInput as ah, type TrajectoryStateChangeInput as ai, type PlaybackState as aj, type TrajectoryFrame as ak, type FrameCaptureOptions as al, type FrameCaptureResult as am, type FrameCaptureBlobResult as an, type FrameCaptureAPI as ao, type CameraFrameCaptureOptions as ap, type CameraFrameCaptureAPI as aq, type Cameras as ar, type CameraFrameSequenceCamera as as, type CameraFrameCaptureSource as at, type CameraFrameSequenceOptions as au, type CameraFrameSequenceResult as av, type PolicyCameraFrameStream as aw, type PolicyCameraFrameCaptureOptions as ax, type PolicyCameraFrameCaptureResult as ay, type FrameCaptureStatus as az, type MujocoSimAPI as b, type Keyframes as b0, ModelActuators as b1, ModelBodies as b2, ModelCameras as b3, ModelGeoms as b4, ModelJoints as b5, ModelKeyframes as b6, type ModelOptions as b7, type ModelResource as b8, ModelResources as b9, type ScenarioCameraConfig as bA, type ScenarioMaterialConfig as bB, type SceneMarker as bC, type SceneObject as bD, type SensorResult as bE, type SiteInfo as bF, type SplatAssetConfig as bG, type SplatScenarioConfig as bH, type StateSnapshot as bI, type TrajectoryData as bJ, type TrajectoryFrameCallbackInput as bK, type VisualScenarioMaterialFilterInput as bL, type XmlPatch as bM, getContact as bN, registerModelResources as bO, ModelSensors as ba, ModelSites as bb, type Models as bc, type MujocoContact as bd, type MujocoContactArray as be, type MujocoFrameCaptureOptions as bf, type ObservationLayoutItem as bg, type PhysicsConfig as bh, type PhysicsStepInput as bi, type PolicyActionChunk as bj, type PolicyActionInput as bk, type PolicyInferenceInput as bl, type PolicyInferenceOutput as bm, type PolicyInferenceResult as bn, type PolicyObservationInput as bo, type RayHit as bp, type Register as bq, type RegisteredModelMap as br, type RemotePolicyRequestInfo as bs, type RemotePolicyRequestInput as bt, type RemotePolicyResponseInfo as bu, type RemotePolicyStatus as bv, type ResetCallbackInput as bw, type ResolvedScenarioCameraConfig as bx, type ResolvedScenarioMaterialConfig as by, type ResourceSelector as bz, type StepCallbackInput as c, type SelectionCallbackInput as d, type MujocoModule as e, type MujocoRenderOptions as f, type MujocoModel as g, type MujocoData as h, type ControlGroupSelector as i, type ObservationResult as j, type IkContextValue as k, type IkGizmoProps as l, type SceneLightsProps as m, type ScenarioLightingProps as n, type SplatEnvironmentProps as o, type VisualScenarioConfig as p, type SplatRendererKind as q, type PairedSplatEnvironmentConfig as r, type SplatFormat as s, type SplatCollisionProxyConfig as t, type SplatEnvironmentReadiness as u, type SplatCollisionPrimitive as v, SplatEnvironmentReadinessStatus as w, type SplatSceneConfigInput as x, type SplatSceneConfigState as y, type VisualScenarioExecutionContextInput as z };
|
package/package.json
CHANGED
package/src/components/Debug.tsx
CHANGED
|
@@ -13,7 +13,7 @@ import { useMujocoContext } from '../core/MujocoSimProvider';
|
|
|
13
13
|
import { getName } from '../core/SceneLoader';
|
|
14
14
|
import { CAPTURE_EXCLUDE_KEY } from '../rendering/cameraFrameCapture';
|
|
15
15
|
import { getContact, withContacts } from '../types';
|
|
16
|
-
import type { DebugProps } from '../types';
|
|
16
|
+
import type { CameraFrameCaptureVector3, DebugProps, DebugVirtualCamera } from '../types';
|
|
17
17
|
|
|
18
18
|
const JOINT_COLORS: Record<number, number> = {
|
|
19
19
|
0: 0xff0000, // free - red
|
|
@@ -41,6 +41,170 @@ type CameraDebugObject = THREE.Group & {
|
|
|
41
41
|
};
|
|
42
42
|
};
|
|
43
43
|
|
|
44
|
+
function toVector3(
|
|
45
|
+
value: CameraFrameCaptureVector3 | undefined,
|
|
46
|
+
fallback: THREE.Vector3
|
|
47
|
+
) {
|
|
48
|
+
if (!value) return fallback.clone();
|
|
49
|
+
return value instanceof THREE.Vector3
|
|
50
|
+
? value.clone()
|
|
51
|
+
: new THREE.Vector3(value[0], value[1], value[2]);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function createCameraLabel(text: string, color: THREE.ColorRepresentation) {
|
|
55
|
+
const canvas = document.createElement('canvas');
|
|
56
|
+
canvas.width = 256;
|
|
57
|
+
canvas.height = 64;
|
|
58
|
+
const ctx = canvas.getContext('2d')!;
|
|
59
|
+
ctx.fillStyle = new THREE.Color(color).getStyle();
|
|
60
|
+
ctx.font = 'bold 32px monospace';
|
|
61
|
+
ctx.textAlign = 'center';
|
|
62
|
+
ctx.fillText(text, 128, 42);
|
|
63
|
+
const texture = new THREE.CanvasTexture(canvas);
|
|
64
|
+
const sprite = new THREE.Sprite(
|
|
65
|
+
new THREE.SpriteMaterial({
|
|
66
|
+
map: texture,
|
|
67
|
+
depthTest: false,
|
|
68
|
+
transparent: true,
|
|
69
|
+
})
|
|
70
|
+
);
|
|
71
|
+
sprite.position.set(0, 0.014, 0.01);
|
|
72
|
+
sprite.scale.set(0.05, 0.012, 1);
|
|
73
|
+
sprite.renderOrder = 999;
|
|
74
|
+
return sprite;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function createVirtualCameraDebugObject(
|
|
78
|
+
camera: DebugVirtualCamera,
|
|
79
|
+
index: number
|
|
80
|
+
) {
|
|
81
|
+
const color = camera.color ?? '#ff3d71';
|
|
82
|
+
const aimColor = camera.aimColor ?? '#38bdf8';
|
|
83
|
+
const markerScale = camera.markerScale ?? 1;
|
|
84
|
+
const cameraPosition = toVector3(camera.position, new THREE.Vector3());
|
|
85
|
+
const configuredUp = toVector3(camera.up, new THREE.Vector3(0, 0, 1)).normalize();
|
|
86
|
+
const cameraQuaternion = new THREE.Quaternion();
|
|
87
|
+
const forward = new THREE.Vector3();
|
|
88
|
+
|
|
89
|
+
if (camera.quaternion) {
|
|
90
|
+
if (camera.quaternion instanceof THREE.Quaternion) {
|
|
91
|
+
cameraQuaternion.copy(camera.quaternion);
|
|
92
|
+
} else {
|
|
93
|
+
cameraQuaternion.set(
|
|
94
|
+
camera.quaternion[0],
|
|
95
|
+
camera.quaternion[1],
|
|
96
|
+
camera.quaternion[2],
|
|
97
|
+
camera.quaternion[3]
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
forward.set(0, 0, -1).applyQuaternion(cameraQuaternion).normalize();
|
|
101
|
+
} else {
|
|
102
|
+
const target = toVector3(
|
|
103
|
+
camera.lookAt,
|
|
104
|
+
cameraPosition.clone().add(new THREE.Vector3(0, 0, -1))
|
|
105
|
+
);
|
|
106
|
+
forward.copy(target).sub(cameraPosition);
|
|
107
|
+
if (forward.lengthSq() < 1e-8) forward.set(0, 0, -1);
|
|
108
|
+
forward.normalize();
|
|
109
|
+
cameraQuaternion.setFromRotationMatrix(
|
|
110
|
+
new THREE.Matrix4().lookAt(cameraPosition, target, configuredUp)
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const target = camera.lookAt
|
|
115
|
+
? toVector3(camera.lookAt, cameraPosition.clone().add(forward))
|
|
116
|
+
: cameraPosition.clone().addScaledVector(forward, 0.4);
|
|
117
|
+
const distanceToTarget = Math.max(target.distanceTo(cameraPosition), 0.001);
|
|
118
|
+
const depth = camera.frustumDepth ?? Math.min(Math.max(distanceToTarget * 0.42, 0.16), 0.45);
|
|
119
|
+
const fov = camera.fov ?? 50;
|
|
120
|
+
const aspect = (camera.width ?? 640) / (camera.height ?? 480);
|
|
121
|
+
|
|
122
|
+
const right = forward.clone().cross(configuredUp);
|
|
123
|
+
if (right.lengthSq() < 1e-8) right.set(1, 0, 0);
|
|
124
|
+
right.normalize();
|
|
125
|
+
const orthogonalUp = right.clone().cross(forward).normalize();
|
|
126
|
+
const frustumHeight = 2 * Math.tan(THREE.MathUtils.degToRad(fov) / 2) * depth;
|
|
127
|
+
const frustumWidth = frustumHeight * aspect;
|
|
128
|
+
const center = cameraPosition.clone().addScaledVector(forward, depth);
|
|
129
|
+
const halfRight = right.clone().multiplyScalar(frustumWidth / 2);
|
|
130
|
+
const halfUp = orthogonalUp.clone().multiplyScalar(frustumHeight / 2);
|
|
131
|
+
|
|
132
|
+
const topLeft = center.clone().sub(halfRight).add(halfUp);
|
|
133
|
+
const topRight = center.clone().add(halfRight).add(halfUp);
|
|
134
|
+
const bottomRight = center.clone().add(halfRight).sub(halfUp);
|
|
135
|
+
const bottomLeft = center.clone().sub(halfRight).sub(halfUp);
|
|
136
|
+
const frustumPoints = [
|
|
137
|
+
cameraPosition, topLeft,
|
|
138
|
+
cameraPosition, topRight,
|
|
139
|
+
cameraPosition, bottomRight,
|
|
140
|
+
cameraPosition, bottomLeft,
|
|
141
|
+
topLeft, topRight,
|
|
142
|
+
topRight, bottomRight,
|
|
143
|
+
bottomRight, bottomLeft,
|
|
144
|
+
bottomLeft, topLeft,
|
|
145
|
+
];
|
|
146
|
+
|
|
147
|
+
const group = new THREE.Group();
|
|
148
|
+
group.name = camera.name ?? `virtual-camera-${index}`;
|
|
149
|
+
group.renderOrder = 999;
|
|
150
|
+
group.frustumCulled = false;
|
|
151
|
+
|
|
152
|
+
const frustum = new THREE.LineSegments(
|
|
153
|
+
new THREE.BufferGeometry().setFromPoints(frustumPoints),
|
|
154
|
+
new THREE.LineBasicMaterial({
|
|
155
|
+
color,
|
|
156
|
+
transparent: true,
|
|
157
|
+
opacity: 0.9,
|
|
158
|
+
depthTest: false,
|
|
159
|
+
})
|
|
160
|
+
);
|
|
161
|
+
frustum.renderOrder = 999;
|
|
162
|
+
frustum.frustumCulled = false;
|
|
163
|
+
group.add(frustum);
|
|
164
|
+
|
|
165
|
+
const aim = new THREE.LineSegments(
|
|
166
|
+
new THREE.BufferGeometry().setFromPoints([cameraPosition, target]),
|
|
167
|
+
new THREE.LineBasicMaterial({
|
|
168
|
+
color: aimColor,
|
|
169
|
+
transparent: true,
|
|
170
|
+
opacity: 0.95,
|
|
171
|
+
depthTest: false,
|
|
172
|
+
})
|
|
173
|
+
);
|
|
174
|
+
aim.renderOrder = 999;
|
|
175
|
+
aim.frustumCulled = false;
|
|
176
|
+
group.add(aim);
|
|
177
|
+
|
|
178
|
+
const markerGroup = new THREE.Group();
|
|
179
|
+
markerGroup.position.copy(cameraPosition);
|
|
180
|
+
markerGroup.quaternion.copy(cameraQuaternion);
|
|
181
|
+
markerGroup.renderOrder = 999;
|
|
182
|
+
markerGroup.frustumCulled = false;
|
|
183
|
+
markerGroup.add(new THREE.Mesh(
|
|
184
|
+
new THREE.BoxGeometry(0.045 * markerScale, 0.028 * markerScale, 0.022 * markerScale),
|
|
185
|
+
new THREE.MeshBasicMaterial({ color, depthTest: false })
|
|
186
|
+
));
|
|
187
|
+
const lens = new THREE.Mesh(
|
|
188
|
+
new THREE.BoxGeometry(0.025 * markerScale, 0.018 * markerScale, 0.014 * markerScale),
|
|
189
|
+
new THREE.MeshBasicMaterial({ color: aimColor, depthTest: false })
|
|
190
|
+
);
|
|
191
|
+
lens.position.set(0, 0, -0.021 * markerScale);
|
|
192
|
+
markerGroup.add(lens);
|
|
193
|
+
if (camera.name) markerGroup.add(createCameraLabel(camera.name, color));
|
|
194
|
+
group.add(markerGroup);
|
|
195
|
+
|
|
196
|
+
const targetMarker = new THREE.Mesh(
|
|
197
|
+
new THREE.SphereGeometry(0.018 * markerScale, 16, 10),
|
|
198
|
+
new THREE.MeshBasicMaterial({ color: aimColor, depthTest: false })
|
|
199
|
+
);
|
|
200
|
+
targetMarker.position.copy(target);
|
|
201
|
+
targetMarker.renderOrder = 999;
|
|
202
|
+
targetMarker.frustumCulled = false;
|
|
203
|
+
group.add(targetMarker);
|
|
204
|
+
|
|
205
|
+
return group;
|
|
206
|
+
}
|
|
207
|
+
|
|
44
208
|
/**
|
|
45
209
|
* Declarative debug visualization component.
|
|
46
210
|
* Renders wireframe geoms, site markers, joint axes, contact forces, COM markers, etc.
|
|
@@ -50,6 +214,7 @@ export function Debug({
|
|
|
50
214
|
showSites = false,
|
|
51
215
|
showJoints = false,
|
|
52
216
|
showCameras = false,
|
|
217
|
+
virtualCameras = [],
|
|
53
218
|
showContacts = false,
|
|
54
219
|
showCOM = false,
|
|
55
220
|
showInertia = false,
|
|
@@ -69,6 +234,7 @@ export function Debug({
|
|
|
69
234
|
const sites: THREE.Object3D[] = [];
|
|
70
235
|
const joints: THREE.Object3D[] = [];
|
|
71
236
|
const cameras: CameraDebugObject[] = [];
|
|
237
|
+
const virtualCameraObjects: THREE.Object3D[] = [];
|
|
72
238
|
const comMarkers: THREE.Object3D[] = [];
|
|
73
239
|
|
|
74
240
|
// Wireframe geoms
|
|
@@ -270,6 +436,10 @@ export function Debug({
|
|
|
270
436
|
}
|
|
271
437
|
}
|
|
272
438
|
|
|
439
|
+
for (let i = 0; i < virtualCameras.length; i += 1) {
|
|
440
|
+
virtualCameraObjects.push(createVirtualCameraDebugObject(virtualCameras[i], i));
|
|
441
|
+
}
|
|
442
|
+
|
|
273
443
|
// COM markers
|
|
274
444
|
if (showCOM) {
|
|
275
445
|
for (let i = 1; i < model.nbody; i++) {
|
|
@@ -281,8 +451,8 @@ export function Debug({
|
|
|
281
451
|
}
|
|
282
452
|
}
|
|
283
453
|
|
|
284
|
-
return { geoms, sites, joints, cameras, comMarkers };
|
|
285
|
-
}, [status, mjModelRef, showGeoms, showSites, showJoints, showCameras, showCOM]);
|
|
454
|
+
return { geoms, sites, joints, cameras, virtualCameraObjects, comMarkers };
|
|
455
|
+
}, [status, mjModelRef, showGeoms, showSites, showJoints, showCameras, virtualCameras, showCOM]);
|
|
286
456
|
|
|
287
457
|
// Add/remove debug objects from scene
|
|
288
458
|
useEffect(() => {
|
|
@@ -294,6 +464,7 @@ export function Debug({
|
|
|
294
464
|
...debugGeometry.sites,
|
|
295
465
|
...debugGeometry.joints,
|
|
296
466
|
...debugGeometry.cameras,
|
|
467
|
+
...debugGeometry.virtualCameraObjects,
|
|
297
468
|
...debugGeometry.comMarkers,
|
|
298
469
|
];
|
|
299
470
|
for (const obj of allObjects) group.add(obj);
|