mujoco-react 11.0.0 → 12.1.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 +25 -18
- package/dist/chunk-U2Y2BODT.js +137 -0
- package/dist/chunk-U2Y2BODT.js.map +1 -0
- package/dist/{chunk-UCQ5MJP4.js → chunk-XD3AY7XZ.js} +47 -9
- package/dist/chunk-XD3AY7XZ.js.map +1 -0
- package/dist/index.d.ts +10 -15
- package/dist/index.js +1437 -1090
- package/dist/index.js.map +1 -1
- package/dist/onnx.d.ts +1 -1
- package/dist/physics.worker.d.ts +2 -0
- package/dist/physics.worker.js +78 -0
- package/dist/physics.worker.js.map +1 -0
- package/dist/spark.d.ts +1 -1
- package/dist/spark.js +1 -1
- package/dist/{types-BP8iiYei.d.ts → types-CSi3fIVs.d.ts} +81 -1
- package/dist/vite.d.ts +5 -2
- package/dist/vite.js +6 -2
- package/dist/vite.js.map +1 -1
- package/package.json +6 -3
- package/src/components/CameraView.tsx +9 -7
- package/src/core/MujocoCanvas.tsx +2 -0
- package/src/core/MujocoPhysics.tsx +1 -0
- package/src/core/MujocoSimProvider.tsx +103 -81
- package/src/core/sceneIdentity.ts +15 -0
- package/src/core/stepBudget.ts +18 -0
- package/src/hooks/useCameraStream.ts +14 -17
- package/src/hooks/usePolicy.ts +16 -7
- package/src/index.ts +3 -0
- package/src/physics.worker.ts +13 -0
- package/src/rendering/cameraFrameCapture.ts +33 -7
- package/src/runtime/client.ts +186 -0
- package/src/runtime/engine.ts +93 -0
- package/src/runtime/protocol.ts +59 -0
- package/src/runtime/types.ts +51 -0
- package/src/types.ts +19 -0
- package/src/vite.ts +7 -2
- package/dist/chunk-UCQ5MJP4.js.map +0 -1
package/README.md
CHANGED
|
@@ -10,26 +10,25 @@ Composable [React Three Fiber](https://docs.pmnd.rs/react-three-fiber) wrapper a
|
|
|
10
10
|
|
|
11
11
|
**[Demo](https://mujoco-react-example.pages.dev)** | **[Docs](https://dadd.mintlify.app)** | **[npm](https://www.npmjs.com/package/mujoco-react)** | **[Example Source](https://github.com/noah-wardlow/mujoco-react-example)** | **[llms.txt](https://dadd.mintlify.app/llms.txt)**
|
|
12
12
|
|
|
13
|
-
## MuJoCo 3.15 and analytic IK
|
|
14
|
-
|
|
15
|
-
The engine dependency is pinned to `@mujoco/mujoco@3.15.0` so upstream minor releases
|
|
16
|
-
cannot silently change model behavior.
|
|
17
|
-
|
|
18
|
-
- `GenericIK` uses `mj_jacSite`; `solveJoints()` supports ball/free joints through `mj_integratePos` (see [IK docs](docs/api/ik-control.mdx)). Existing scalar solve calls remain supported; `epsilon` is ignored.
|
|
19
|
-
- Actuator IDs and control addresses are distinct. `useActuators()` / `getActuators()` expose `ctrlAdr`, `ctrlCount`, and per-input `ranges`. Use `api.setCtrl('pid_servo', [position, velocity, feedforward])` or `useCtrl(name, inputIndex)` for multi-input actuators. Scalar hooks/groups reject ambiguous multi-input commands; low-level policy vectors remain flat control vectors.
|
|
20
|
-
- `<MujocoProvider threadCount={2} ...>` initializes an engine thread pool for each simulation when using the opt-in threaded build. This parallelizes engine work; the main `mj_step` call still runs on the UI thread.
|
|
21
|
-
- `<MujocoCanvas integrator="discrete">` and `<MujocoPhysics integrator="discrete">` support the new integrator without changing MJCF defaults when omitted.
|
|
22
|
-
- Snapshots include MuJoCo integration state, including mocap, history, applied forces, and warmstart. They belong to the loaded model; save again after a scene reload. Legacy snapshots remain accepted. Experimental IPC flex contact is not exactly replayable because MuJoCo does not expose all of its state.
|
|
23
|
-
- `FlexRenderer` draws indexed 2D surfaces and 3D boundary surfaces, plus 1D lines.
|
|
24
|
-
|
|
25
|
-
Review [upstream migration notes](https://mujoco.readthedocs.io/en/3.15.0/changelog.html): actuator damping and flex integration have changed since 3.9. Scalar `data.ctrl[actuator.id]` consumers should use `data.ctrl[actuator.ctrlAdr]`; multi-input controls occupy `ctrlCount` consecutive entries. `homeJoints` is limited to scalar-input models; use MJCF initial poses/keyframes and explicit controls for multi-input models.
|
|
26
|
-
|
|
27
13
|
## Install
|
|
28
14
|
|
|
29
15
|
```bash
|
|
30
16
|
npm install mujoco-react three @react-three/fiber @react-three/drei
|
|
31
17
|
```
|
|
32
18
|
|
|
19
|
+
## Running policies and simulation workloads
|
|
20
|
+
|
|
21
|
+
Use `const runtime = await api.createRuntime()` after `onReady`, then await
|
|
22
|
+
`runtime.observe()` and `runtime.advance({ steps, controls })`. Physics runs in a
|
|
23
|
+
dedicated worker; commands reject when busy instead of accumulating. The runtime
|
|
24
|
+
supports cancellation, reset, full snapshots, synchronized camera observations,
|
|
25
|
+
and separate physics/capture timing. Use `<CameraView>` for GPU-only previews.
|
|
26
|
+
|
|
27
|
+
The viewer has an 8 ms / 100-step per-frame budget. Equivalent inline scene
|
|
28
|
+
configs preserve the loaded model. See the [simulation runtime guide](https://dadd.mintlify.app/guides/simulation-runtime)
|
|
29
|
+
for ownership, VLA loops, bundling, and performance limits, or try the
|
|
30
|
+
[worker demo](https://mujoco-react-example.pages.dev/?demo=runtime).
|
|
31
|
+
|
|
33
32
|
## Vite Plugin and Type-Safe Names
|
|
34
33
|
|
|
35
34
|
Use the Vite plugin to generate declaration merging for actuator, sensor, body, joint, site, geom, and keyframe names:
|
|
@@ -50,6 +49,10 @@ export default defineConfig({
|
|
|
50
49
|
});
|
|
51
50
|
```
|
|
52
51
|
|
|
52
|
+
The plugin also preserves worker asset URLs in Vite development by excluding
|
|
53
|
+
`mujoco-react` from dependency prebundling. Without it, set
|
|
54
|
+
`optimizeDeps: { exclude: ["mujoco-react"] }` when using the worker runtime.
|
|
55
|
+
|
|
53
56
|
The plugin writes `src/mujoco-register.gen.ts` during dev and build. Commit that generated file. Vite auto-loads it, so app code imports generated values from `mujoco-react`, not from the generated file:
|
|
54
57
|
|
|
55
58
|
```ts
|
|
@@ -1167,8 +1170,8 @@ const frame = await apiRef.current?.captureCameraFrame({
|
|
|
1167
1170
|
```
|
|
1168
1171
|
|
|
1169
1172
|
**Live streams.** `useCameraStream(canvasRef, options)` renders a camera into a
|
|
1170
|
-
DOM `<canvas>`
|
|
1171
|
-
|
|
1173
|
+
DOM `<canvas>` at 10 FPS by default (offscreen render → GPU readback → blit).
|
|
1174
|
+
It avoids PNG encoding but still pays readback and pixel-conversion costs. Call it inside `<MujocoCanvas>`; the
|
|
1172
1175
|
canvas can live anywhere in your DOM:
|
|
1173
1176
|
|
|
1174
1177
|
```tsx
|
|
@@ -1181,8 +1184,8 @@ function WristStream({ canvasRef }) {
|
|
|
1181
1184
|
For a transparent picture-in-picture overlay, use `<CameraView>` (or
|
|
1182
1185
|
`useCameraViewport` to track your own element). It scissors into the main canvas —
|
|
1183
1186
|
cheaper, but it takes over the render loop while mounted (incompatible with
|
|
1184
|
-
postprocessing, occluded by opaque DOM), so
|
|
1185
|
-
|
|
1187
|
+
postprocessing, occluded by opaque DOM), so use `useCameraStream` only when HTML compositing requires a separate canvas.
|
|
1188
|
+
Prefer GPU viewports for live simulation previews:
|
|
1186
1189
|
|
|
1187
1190
|
```tsx
|
|
1188
1191
|
<MujocoCanvas config={config}>
|
|
@@ -1427,3 +1430,7 @@ These MuJoCo features are not yet exposed in the WASM binding:
|
|
|
1427
1430
|
## License
|
|
1428
1431
|
|
|
1429
1432
|
Apache-2.0
|
|
1433
|
+
|
|
1434
|
+
## Contacts and plasticity
|
|
1435
|
+
|
|
1436
|
+
Use MJCF speculative contacts for fast rigid objects and yielding springs for permanent deformation. The existing model/data interface exposes enable flags and plastic state; full snapshots preserve plastic history. See the [guide](docs/guides/engine-features.mdx) and [interactive demos](https://mujoco-react-example.pages.dev/?demo=speculative).
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// src/runtime/engine.ts
|
|
2
|
+
var displayFields = [
|
|
3
|
+
"qpos",
|
|
4
|
+
"qvel",
|
|
5
|
+
"ctrl",
|
|
6
|
+
"act",
|
|
7
|
+
"plastic",
|
|
8
|
+
"sensordata",
|
|
9
|
+
"xpos",
|
|
10
|
+
"xquat",
|
|
11
|
+
"xmat",
|
|
12
|
+
"geom_xpos",
|
|
13
|
+
"geom_xmat",
|
|
14
|
+
"site_xpos",
|
|
15
|
+
"site_xmat",
|
|
16
|
+
"cam_xpos",
|
|
17
|
+
"cam_xmat",
|
|
18
|
+
"flexvert_xpos",
|
|
19
|
+
"ten_length",
|
|
20
|
+
"wrap_xpos",
|
|
21
|
+
"cvel",
|
|
22
|
+
"cfrc_ext",
|
|
23
|
+
"qfrc_applied",
|
|
24
|
+
"xfrc_applied",
|
|
25
|
+
"qfrc_bias",
|
|
26
|
+
"ten_wrapadr",
|
|
27
|
+
"ten_wrapnum"
|
|
28
|
+
];
|
|
29
|
+
var SimulationEngine = class {
|
|
30
|
+
constructor(mj, initial) {
|
|
31
|
+
this.mj = mj;
|
|
32
|
+
this.initial = initial;
|
|
33
|
+
mj.FS.writeFile("/runtime.mjb", initial.model);
|
|
34
|
+
const vfs = new mj.MjVFS();
|
|
35
|
+
try {
|
|
36
|
+
this.model = mj.MjModel.from_binary_path("/runtime.mjb", vfs);
|
|
37
|
+
} finally {
|
|
38
|
+
vfs.delete();
|
|
39
|
+
mj.FS.unlink("/runtime.mjb");
|
|
40
|
+
}
|
|
41
|
+
this.data = new mj.MjData(this.model);
|
|
42
|
+
try {
|
|
43
|
+
this.reset();
|
|
44
|
+
} catch (error) {
|
|
45
|
+
this.dispose();
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
model;
|
|
50
|
+
data;
|
|
51
|
+
reset() {
|
|
52
|
+
this.mj.mj_resetData(this.model, this.data);
|
|
53
|
+
this.restore(this.initial.state);
|
|
54
|
+
}
|
|
55
|
+
restore(state) {
|
|
56
|
+
if (state.signature !== this.mj.mjtState.mjSTATE_INTEGRATION.value || state.values.length !== this.mj.mj_stateSize(this.model, state.signature) || !state.values.every(Number.isFinite)) throw new Error("Invalid runtime snapshot");
|
|
57
|
+
this.mj.mj_setState(this.model, this.data, Array.from(state.values), state.signature);
|
|
58
|
+
this.mj.mj_forward(this.model, this.data);
|
|
59
|
+
}
|
|
60
|
+
setControls(controls) {
|
|
61
|
+
if (controls) {
|
|
62
|
+
if (controls.length !== this.data.ctrl.length || !controls.every(Number.isFinite)) throw new Error("Invalid runtime controls");
|
|
63
|
+
this.data.ctrl.set(controls);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
step() {
|
|
67
|
+
const expected = this.data.time + this.model.opt.timestep;
|
|
68
|
+
this.mj.mj_step(this.model, this.data);
|
|
69
|
+
if (Math.abs(this.data.time - expected) > 1e-7 || !this.data.qpos.every(Number.isFinite)) {
|
|
70
|
+
throw new Error("Physics became unstable or reset unexpectedly");
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
save() {
|
|
74
|
+
const signature = this.mj.mjtState.mjSTATE_INTEGRATION.value;
|
|
75
|
+
const buffer = new this.mj.DoubleBuffer(this.mj.mj_stateSize(this.model, signature));
|
|
76
|
+
try {
|
|
77
|
+
this.mj.mj_getState(this.model, this.data, buffer, signature);
|
|
78
|
+
return { signature, values: new Float64Array(buffer.GetView()) };
|
|
79
|
+
} finally {
|
|
80
|
+
buffer.delete();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
snapshot(complete = true) {
|
|
84
|
+
const integration = complete ? this.save() : void 0;
|
|
85
|
+
if (integration) this.mj.mj_forward(this.model, this.data);
|
|
86
|
+
try {
|
|
87
|
+
return {
|
|
88
|
+
time: this.data.time,
|
|
89
|
+
fields: Object.fromEntries(displayFields.map((key) => [key, new Float64Array(this.data[key])])),
|
|
90
|
+
contacts: complete ? this.contacts() : [],
|
|
91
|
+
...integration ? { integration } : {}
|
|
92
|
+
};
|
|
93
|
+
} finally {
|
|
94
|
+
if (integration) this.mj.mj_setState(this.model, this.data, Array.from(integration.values), integration.signature);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
contacts() {
|
|
98
|
+
const result = [], vector = this.data.contact;
|
|
99
|
+
const names = this.model.names, addresses = this.model.name_geomadr;
|
|
100
|
+
const name = (id) => {
|
|
101
|
+
if (id < 0) return "flex";
|
|
102
|
+
const start = addresses[id];
|
|
103
|
+
let end = start;
|
|
104
|
+
while (end < names.length && names[end]) end++;
|
|
105
|
+
return new TextDecoder().decode(names.subarray(start, end));
|
|
106
|
+
};
|
|
107
|
+
try {
|
|
108
|
+
for (let i = 0; i < this.data.ncon; i++) {
|
|
109
|
+
const c = vector.get(i);
|
|
110
|
+
if (!c) continue;
|
|
111
|
+
try {
|
|
112
|
+
result.push({
|
|
113
|
+
geom1: c.geom1,
|
|
114
|
+
geom2: c.geom2,
|
|
115
|
+
geom1Name: name(c.geom1),
|
|
116
|
+
geom2Name: name(c.geom2),
|
|
117
|
+
pos: Array.from(c.pos),
|
|
118
|
+
depth: c.dist
|
|
119
|
+
});
|
|
120
|
+
} finally {
|
|
121
|
+
c.delete();
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
} finally {
|
|
125
|
+
vector.delete();
|
|
126
|
+
}
|
|
127
|
+
return result;
|
|
128
|
+
}
|
|
129
|
+
dispose() {
|
|
130
|
+
this.data.delete();
|
|
131
|
+
this.model.delete();
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
export { SimulationEngine, displayFields };
|
|
136
|
+
//# sourceMappingURL=chunk-U2Y2BODT.js.map
|
|
137
|
+
//# sourceMappingURL=chunk-U2Y2BODT.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/runtime/engine.ts"],"names":[],"mappings":";AAGO,IAAM,aAAA,GAAgB;AAAA,EAAC,MAAA;AAAA,EAAQ,MAAA;AAAA,EAAQ,MAAA;AAAA,EAAQ,KAAA;AAAA,EAAO,SAAA;AAAA,EAAW,YAAA;AAAA,EAAc,MAAA;AAAA,EAAQ,OAAA;AAAA,EAAS,MAAA;AAAA,EACrG,WAAA;AAAA,EAAa,WAAA;AAAA,EAAa,WAAA;AAAA,EAAa,WAAA;AAAA,EAAa,UAAA;AAAA,EAAY,UAAA;AAAA,EAChE,eAAA;AAAA,EAAiB,YAAA;AAAA,EAAc,WAAA;AAAA,EAAa,MAAA;AAAA,EAAQ,UAAA;AAAA,EAAY,cAAA;AAAA,EAAgB,cAAA;AAAA,EAAgB,WAAA;AAAA,EAAa,aAAA;AAAA,EAAe;AAAa;AASpI,IAAM,mBAAN,MAAuB;AAAA,EAG5B,WAAA,CAAoB,IAAwB,OAAA,EAA+B;AAAvD,IAAA,IAAA,CAAA,EAAA,GAAA,EAAA;AAAwB,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AAC1C,IAAA,EAAA,CAAG,EAAA,CAAG,SAAA,CAAU,cAAA,EAAgB,OAAA,CAAQ,KAAK,CAAA;AAC7C,IAAA,MAAM,GAAA,GAAM,IAAI,EAAA,CAAG,KAAA,EAAM;AACzB,IAAA,IAAI;AAAE,MAAA,IAAA,CAAK,KAAA,GAAQ,EAAA,CAAG,OAAA,CAAQ,gBAAA,CAAiB,gBAAgB,GAAG,CAAA;AAAA,IAAG,CAAA,SACrE;AAAU,MAAA,GAAA,CAAI,MAAA,EAAO;AAAG,MAAA,EAAA,CAAG,EAAA,CAAG,OAAO,cAAc,CAAA;AAAA,IAAG;AACtD,IAAA,IAAA,CAAK,IAAA,GAAO,IAAI,EAAA,CAAG,MAAA,CAAO,KAAK,KAAK,CAAA;AACpC,IAAA,IAAI;AAAE,MAAA,IAAA,CAAK,KAAA,EAAM;AAAA,IAAG,SAAS,KAAA,EAAO;AAAE,MAAA,IAAA,CAAK,OAAA,EAAQ;AAAG,MAAA,MAAM,KAAA;AAAA,IAAO;AAAA,EACrE;AAAA,EATS,KAAA;AAAA,EACA,IAAA;AAAA,EAST,KAAA,GAAQ;AACN,IAAA,IAAA,CAAK,EAAA,CAAG,YAAA,CAAa,IAAA,CAAK,KAAA,EAAO,KAAK,IAAI,CAAA;AAC1C,IAAA,IAAA,CAAK,OAAA,CAAQ,IAAA,CAAK,OAAA,CAAQ,KAAK,CAAA;AAAA,EACjC;AAAA,EACA,QAAQ,KAAA,EAAoB;AAC1B,IAAA,IAAI,KAAA,CAAM,SAAA,KAAc,IAAA,CAAK,EAAA,CAAG,QAAA,CAAS,mBAAA,CAAoB,KAAA,IAC3D,KAAA,CAAM,MAAA,CAAO,MAAA,KAAW,IAAA,CAAK,EAAA,CAAG,YAAA,CAAa,IAAA,CAAK,KAAA,EAAO,KAAA,CAAM,SAAS,CAAA,IACxE,CAAC,KAAA,CAAM,MAAA,CAAO,KAAA,CAAM,MAAA,CAAO,QAAQ,CAAA,EAAG,MAAM,IAAI,KAAA,CAAM,0BAA0B,CAAA;AAClF,IAAA,IAAA,CAAK,EAAA,CAAG,WAAA,CAAY,IAAA,CAAK,KAAA,EAAO,IAAA,CAAK,IAAA,EAAM,KAAA,CAAM,IAAA,CAAK,KAAA,CAAM,MAAM,CAAA,EAAG,KAAA,CAAM,SAAS,CAAA;AACpF,IAAA,IAAA,CAAK,EAAA,CAAG,UAAA,CAAW,IAAA,CAAK,KAAA,EAAO,KAAK,IAAI,CAAA;AAAA,EAC1C;AAAA,EACA,YAAY,QAAA,EAAyB;AACnC,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA,IAAI,QAAA,CAAS,MAAA,KAAW,IAAA,CAAK,IAAA,CAAK,KAAK,MAAA,IAAU,CAAC,QAAA,CAAS,KAAA,CAAM,OAAO,QAAQ,CAAA,EAAG,MAAM,IAAI,MAAM,0BAA0B,CAAA;AAC7H,MAAA,IAAA,CAAK,IAAA,CAAK,IAAA,CAAK,GAAA,CAAI,QAAQ,CAAA;AAAA,IAC7B;AAAA,EACF;AAAA,EACA,IAAA,GAAO;AACL,IAAA,MAAM,WAAW,IAAA,CAAK,IAAA,CAAK,IAAA,GAAO,IAAA,CAAK,MAAM,GAAA,CAAI,QAAA;AACjD,IAAA,IAAA,CAAK,EAAA,CAAG,OAAA,CAAQ,IAAA,CAAK,KAAA,EAAO,KAAK,IAAI,CAAA;AACrC,IAAA,IAAI,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,IAAA,CAAK,OAAO,QAAQ,CAAA,GAAI,IAAA,IAAQ,CAAC,KAAK,IAAA,CAAK,IAAA,CAAK,KAAA,CAAM,MAAA,CAAO,QAAQ,CAAA,EAAG;AACxF,MAAA,MAAM,IAAI,MAAM,+CAA+C,CAAA;AAAA,IACjE;AAAA,EACF;AAAA,EACA,IAAA,GAAoB;AAClB,IAAA,MAAM,SAAA,GAAY,IAAA,CAAK,EAAA,CAAG,QAAA,CAAS,mBAAA,CAAoB,KAAA;AACvD,IAAA,MAAM,MAAA,GAAS,IAAI,IAAA,CAAK,EAAA,CAAG,YAAA,CAAa,IAAA,CAAK,EAAA,CAAG,YAAA,CAAa,IAAA,CAAK,KAAA,EAAO,SAAS,CAAC,CAAA;AACnF,IAAA,IAAI;AACF,MAAA,IAAA,CAAK,GAAG,WAAA,CAAY,IAAA,CAAK,OAAO,IAAA,CAAK,IAAA,EAAM,QAAQ,SAAS,CAAA;AAC5D,MAAA,OAAO,EAAE,WAAW,MAAA,EAAQ,IAAI,aAAa,MAAA,CAAO,OAAA,EAAS,CAAA,EAAE;AAAA,IACjE,CAAA,SAAE;AAAU,MAAA,MAAA,CAAO,MAAA,EAAO;AAAA,IAAG;AAAA,EAC/B;AAAA,EACA,QAAA,CAAS,WAAW,IAAA,EAAoB;AACtC,IAAA,MAAM,WAAA,GAAc,QAAA,GAAW,IAAA,CAAK,IAAA,EAAK,GAAI,MAAA;AAG7C,IAAA,IAAI,aAAa,IAAA,CAAK,EAAA,CAAG,WAAW,IAAA,CAAK,KAAA,EAAO,KAAK,IAAI,CAAA;AACzD,IAAA,IAAI;AAAE,MAAA,OAAO;AAAA,QACX,IAAA,EAAM,KAAK,IAAA,CAAK,IAAA;AAAA,QAChB,QAAQ,MAAA,CAAO,WAAA,CAAY,aAAA,CAAc,GAAA,CAAI,SAAO,CAAC,GAAA,EAAK,IAAI,YAAA,CAAa,KAAK,IAAA,CAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;AAAA,QAC5F,QAAA,EAAU,QAAA,GAAW,IAAA,CAAK,QAAA,KAAa,EAAC;AAAA,QACxC,GAAI,WAAA,GAAc,EAAE,WAAA,KAAgB;AAAC,OACvC;AAAA,IAAG,CAAA,SAAE;AACH,MAAA,IAAI,WAAA,EAAa,IAAA,CAAK,EAAA,CAAG,WAAA,CAAY,KAAK,KAAA,EAAO,IAAA,CAAK,IAAA,EAAM,KAAA,CAAM,IAAA,CAAK,WAAA,CAAY,MAAM,CAAA,EAAG,YAAY,SAAS,CAAA;AAAA,IACnH;AAAA,EACF;AAAA,EACQ,QAAA,GAA0B;AAChC,IAAA,MAAM,MAAA,GAAwB,EAAC,EAAG,MAAA,GAAS,KAAK,IAAA,CAAK,OAAA;AACrD,IAAA,MAAM,QAAQ,IAAA,CAAK,KAAA,CAAM,KAAA,EAAO,SAAA,GAAY,KAAK,KAAA,CAAM,YAAA;AACvD,IAAA,MAAM,IAAA,GAAO,CAAC,EAAA,KAAe;AAC3B,MAAA,IAAI,EAAA,GAAK,GAAG,OAAO,MAAA;AACnB,MAAA,MAAM,KAAA,GAAQ,UAAU,EAAE,CAAA;AAAG,MAAA,IAAI,GAAA,GAAM,KAAA;AACvC,MAAA,OAAO,GAAA,GAAM,KAAA,CAAM,MAAA,IAAU,KAAA,CAAM,GAAG,CAAA,EAAG,GAAA,EAAA;AACzC,MAAA,OAAO,IAAI,aAAY,CAAE,MAAA,CAAO,MAAM,QAAA,CAAS,KAAA,EAAO,GAAG,CAAC,CAAA;AAAA,IAC5D,CAAA;AACA,IAAA,IAAI;AACF,MAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,IAAA,CAAK,IAAA,CAAK,MAAM,CAAA,EAAA,EAAK;AACvC,QAAA,MAAM,CAAA,GAAI,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA;AACtB,QAAA,IAAI,CAAC,CAAA,EAAG;AACR,QAAA,IAAI;AAAE,UAAA,MAAA,CAAO,IAAA,CAAK;AAAA,YAAE,OAAO,CAAA,CAAE,KAAA;AAAA,YAAO,OAAO,CAAA,CAAE,KAAA;AAAA,YAAO,SAAA,EAAW,IAAA,CAAK,CAAA,CAAE,KAAK,CAAA;AAAA,YAAG,SAAA,EAAW,IAAA,CAAK,CAAA,CAAE,KAAK,CAAA;AAAA,YACnG,GAAA,EAAK,KAAA,CAAM,IAAA,CAAK,CAAA,CAAE,GAAG,CAAA;AAAA,YAA+B,OAAO,CAAA,CAAE;AAAA,WAAM,CAAA;AAAA,QAAG,CAAA,SACxE;AAAU,UAAA,CAAA,CAAE,MAAA,EAAO;AAAA,QAAG;AAAA,MACxB;AAAA,IACF,CAAA,SAAE;AAAU,MAAA,MAAA,CAAO,MAAA,EAAO;AAAA,IAAG;AAC7B,IAAA,OAAO,MAAA;AAAA,EACT;AAAA,EACA,OAAA,GAAU;AAAE,IAAA,IAAA,CAAK,KAAK,MAAA,EAAO;AAAG,IAAA,IAAA,CAAK,MAAM,MAAA,EAAO;AAAA,EAAG;AACvD","file":"chunk-U2Y2BODT.js","sourcesContent":["import type { MainModule, MjModel, MjData } from '@mujoco/mujoco';\nimport type { ContactInfo } from '../types';\n\nexport const displayFields = ['qpos', 'qvel', 'ctrl', 'act', 'plastic', 'sensordata', 'xpos', 'xquat', 'xmat',\n 'geom_xpos', 'geom_xmat', 'site_xpos', 'site_xmat', 'cam_xpos', 'cam_xmat',\n 'flexvert_xpos', 'ten_length', 'wrap_xpos', 'cvel', 'cfrc_ext', 'qfrc_applied', 'xfrc_applied', 'qfrc_bias', 'ten_wrapadr', 'ten_wrapnum'] as const;\nexport interface EngineState { signature: number; values: Float64Array }\nexport interface EngineInitialization { model: Uint8Array; state: EngineState }\nexport interface DisplayState {\n time: number;\n fields: Record<typeof displayFields[number], Float64Array>;\n contacts: ContactInfo[];\n integration?: EngineState;\n}\nexport class SimulationEngine {\n readonly model: MjModel;\n readonly data: MjData;\n constructor(private mj: MainModule, private initial: EngineInitialization) {\n mj.FS.writeFile('/runtime.mjb', initial.model);\n const vfs = new mj.MjVFS();\n try { this.model = mj.MjModel.from_binary_path('/runtime.mjb', vfs); }\n finally { vfs.delete(); mj.FS.unlink('/runtime.mjb'); }\n this.data = new mj.MjData(this.model);\n try { this.reset(); } catch (error) { this.dispose(); throw error; }\n }\n reset() {\n this.mj.mj_resetData(this.model, this.data);\n this.restore(this.initial.state);\n }\n restore(state: EngineState) {\n if (state.signature !== this.mj.mjtState.mjSTATE_INTEGRATION.value ||\n state.values.length !== this.mj.mj_stateSize(this.model, state.signature) ||\n !state.values.every(Number.isFinite)) throw new Error('Invalid runtime snapshot');\n this.mj.mj_setState(this.model, this.data, Array.from(state.values), state.signature);\n this.mj.mj_forward(this.model, this.data);\n }\n setControls(controls?: Float64Array) {\n if (controls) {\n if (controls.length !== this.data.ctrl.length || !controls.every(Number.isFinite)) throw new Error('Invalid runtime controls');\n this.data.ctrl.set(controls);\n }\n }\n step() {\n const expected = this.data.time + this.model.opt.timestep;\n this.mj.mj_step(this.model, this.data);\n if (Math.abs(this.data.time - expected) > 1e-7 || !this.data.qpos.every(Number.isFinite)) {\n throw new Error('Physics became unstable or reset unexpectedly');\n }\n }\n save(): EngineState {\n const signature = this.mj.mjtState.mjSTATE_INTEGRATION.value;\n const buffer = new this.mj.DoubleBuffer(this.mj.mj_stateSize(this.model, signature));\n try {\n this.mj.mj_getState(this.model, this.data, buffer, signature);\n return { signature, values: new Float64Array(buffer.GetView()) };\n } finally { buffer.delete(); }\n }\n snapshot(complete = true): DisplayState {\n const integration = complete ? this.save() : undefined;\n // mj_step integrates qpos after computing derived fields. Refresh completed\n // observations at their reported time, preserving solver warmstart exactly.\n if (integration) this.mj.mj_forward(this.model, this.data);\n try { return {\n time: this.data.time,\n fields: Object.fromEntries(displayFields.map(key => [key, new Float64Array(this.data[key])])) as DisplayState['fields'],\n contacts: complete ? this.contacts() : [],\n ...(integration ? { integration } : {}),\n }; } finally {\n if (integration) this.mj.mj_setState(this.model, this.data, Array.from(integration.values), integration.signature);\n }\n }\n private contacts(): ContactInfo[] {\n const result: ContactInfo[] = [], vector = this.data.contact;\n const names = this.model.names, addresses = this.model.name_geomadr;\n const name = (id: number) => {\n if (id < 0) return 'flex';\n const start = addresses[id]; let end = start;\n while (end < names.length && names[end]) end++;\n return new TextDecoder().decode(names.subarray(start, end));\n };\n try {\n for (let i = 0; i < this.data.ncon; i++) {\n const c = vector.get(i);\n if (!c) continue;\n try { result.push({ geom1: c.geom1, geom2: c.geom2, geom1Name: name(c.geom1), geom2Name: name(c.geom2),\n pos: Array.from(c.pos) as [number, number, number], depth: c.dist }); }\n finally { c.delete(); }\n }\n } finally { vector.delete(); }\n return result;\n }\n dispose() { this.data.delete(); this.model.delete(); }\n}\n"]}
|
|
@@ -315,8 +315,8 @@ function prepareCaptureCamera(camera, options, fallbackCamera, width, height) {
|
|
|
315
315
|
applyProjectionMatrix(camera, options.projectionMatrix);
|
|
316
316
|
applyCameraPose(camera, options, fallbackCamera);
|
|
317
317
|
}
|
|
318
|
-
function readRenderTargetToCanvas(renderer, target, canvas, context, pixels, imageData, width, height, outputColorSpace, flipX = false) {
|
|
319
|
-
renderer.readRenderTargetPixels(target, 0, 0, width, height, pixels);
|
|
318
|
+
function readRenderTargetToCanvas(renderer, target, canvas, context, pixels, imageData, width, height, outputColorSpace, flipX = false, readback = true) {
|
|
319
|
+
if (readback) renderer.readRenderTargetPixels(target, 0, 0, width, height, pixels);
|
|
320
320
|
const rowBytes = width * 4;
|
|
321
321
|
const encodeSrgb = outputColorSpace === THREE.SRGBColorSpace;
|
|
322
322
|
for (let y = 0; y < height; y += 1) {
|
|
@@ -532,6 +532,15 @@ function createCameraFrameCaptureSession(renderer, scene, fallbackCamera, option
|
|
|
532
532
|
const drawContext = context;
|
|
533
533
|
const pixels = new Uint8Array(width * height * 4);
|
|
534
534
|
const imageData = drawContext.createImageData(width, height);
|
|
535
|
+
let inFlight = false, disposed = false;
|
|
536
|
+
function assertAvailable() {
|
|
537
|
+
if (disposed) throw new Error("Camera capture session is disposed");
|
|
538
|
+
if (inFlight) throw new Error("Camera capture session is busy; await the current capture");
|
|
539
|
+
}
|
|
540
|
+
function releaseResources() {
|
|
541
|
+
target.dispose();
|
|
542
|
+
if (isolatedRenderer && !isolatedRenderer.cached) isolatedRenderer.renderer.dispose();
|
|
543
|
+
}
|
|
535
544
|
function resolveCaptureOptions(nextOptions = {}) {
|
|
536
545
|
const captureOptions = { ...options, ...nextOptions };
|
|
537
546
|
if (shouldUseRenderIsolation(captureOptions) !== shouldUseRenderIsolation(options)) {
|
|
@@ -614,9 +623,11 @@ function createCameraFrameCaptureSession(renderer, scene, fallbackCamera, option
|
|
|
614
623
|
};
|
|
615
624
|
}
|
|
616
625
|
function capture(nextOptions = {}) {
|
|
626
|
+
assertAvailable();
|
|
617
627
|
return renderPreparedCapture(resolveCaptureOptions(nextOptions));
|
|
618
628
|
}
|
|
619
629
|
function capturePixels(nextOptions = {}) {
|
|
630
|
+
assertAvailable();
|
|
620
631
|
const captureOptions = resolveCaptureOptions(nextOptions);
|
|
621
632
|
renderCaptureToTarget(captureOptions, () => {
|
|
622
633
|
sessionRenderer.readRenderTargetPixels(target, 0, 0, width, height, pixels);
|
|
@@ -643,6 +654,16 @@ function createCameraFrameCaptureSession(renderer, scene, fallbackCamera, option
|
|
|
643
654
|
return { ...tensor, camera, source: result.source };
|
|
644
655
|
}
|
|
645
656
|
async function captureAsync(nextOptions = {}) {
|
|
657
|
+
assertAvailable();
|
|
658
|
+
inFlight = true;
|
|
659
|
+
try {
|
|
660
|
+
return await captureAsyncInternal(nextOptions);
|
|
661
|
+
} finally {
|
|
662
|
+
inFlight = false;
|
|
663
|
+
if (disposed) releaseResources();
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
async function captureAsyncInternal(nextOptions = {}) {
|
|
646
667
|
const captureOptions = resolveCaptureOptions(nextOptions);
|
|
647
668
|
runCapturePreRenderHooks(scene);
|
|
648
669
|
scene.updateMatrixWorld(true);
|
|
@@ -707,7 +728,25 @@ function createCameraFrameCaptureSession(renderer, scene, fallbackCamera, option
|
|
|
707
728
|
restoreRendererState(sessionRenderer, previousState);
|
|
708
729
|
}
|
|
709
730
|
}
|
|
710
|
-
|
|
731
|
+
let readback;
|
|
732
|
+
renderCaptureToTarget(captureOptions, () => {
|
|
733
|
+
readback = sessionRenderer.readRenderTargetPixelsAsync(target, 0, 0, width, height, pixels);
|
|
734
|
+
});
|
|
735
|
+
await readback;
|
|
736
|
+
readRenderTargetToCanvas(
|
|
737
|
+
sessionRenderer,
|
|
738
|
+
target,
|
|
739
|
+
canvas,
|
|
740
|
+
drawContext,
|
|
741
|
+
pixels,
|
|
742
|
+
imageData,
|
|
743
|
+
width,
|
|
744
|
+
height,
|
|
745
|
+
sessionRenderer.outputColorSpace,
|
|
746
|
+
captureOptions.flipX ?? false,
|
|
747
|
+
false
|
|
748
|
+
);
|
|
749
|
+
return { canvas, camera, width, height, source: getCameraFrameCaptureSource(captureOptions) };
|
|
711
750
|
}
|
|
712
751
|
return {
|
|
713
752
|
width,
|
|
@@ -756,10 +795,9 @@ function createCameraFrameCaptureSession(renderer, scene, fallbackCamera, option
|
|
|
756
795
|
return { ...result, blob, type };
|
|
757
796
|
},
|
|
758
797
|
dispose() {
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
}
|
|
798
|
+
if (disposed) return;
|
|
799
|
+
disposed = true;
|
|
800
|
+
if (!inFlight) releaseResources();
|
|
763
801
|
}
|
|
764
802
|
};
|
|
765
803
|
}
|
|
@@ -1433,5 +1471,5 @@ function clamp01(value) {
|
|
|
1433
1471
|
*/
|
|
1434
1472
|
|
|
1435
1473
|
export { CAMERA_FRAME_CAPTURE_PRE_RENDER_USER_DATA_KEY, CAMERA_FRAME_CAPTURE_RENDER_USER_DATA_KEY, CAPTURE_EXCLUDE_KEY, ModelActuators, ModelBodies, ModelCameras, ModelGeoms, ModelJoints, ModelKeyframes, ModelResources, ModelSensors, ModelSites, ScenarioLighting, SplatEnvironment, SplatEnvironmentReadinessStatus, VisualScenarioEffects, captureCameraFrame, captureCameraFrameBlob, captureCameraFrameTensor, createCameraFrameCaptureSession, createCaptureCamera, createPairedSplatEnvironment, createSparkSplatViewerUrl, createSplatEnvironmentUserData, createSplatSceneConfig, createVisualScenarioExecutionContext, dataUrlToPolicyImageTensor, getContact, getScenarioBackground, getScenarioCameraPosition, getSplatEnvironmentReadiness, imageDataToPolicyImageTensor, pixelsToPolicyImageTensor, prepareCaptureCamera, registerModelResources, renderCameraFrameToCanvas, useSplatEnvironment, useSplatSceneConfig, useVisualScenarioEffects, useVisualScenarioExecutionContext, withContacts, withSplatEnvironment };
|
|
1436
|
-
//# sourceMappingURL=chunk-
|
|
1437
|
-
//# sourceMappingURL=chunk-
|
|
1474
|
+
//# sourceMappingURL=chunk-XD3AY7XZ.js.map
|
|
1475
|
+
//# sourceMappingURL=chunk-XD3AY7XZ.js.map
|