mujoco-react 8.10.0 → 9.0.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 +81 -44
- package/dist/chunk-33CV6HSV.js +400 -0
- package/dist/chunk-33CV6HSV.js.map +1 -0
- package/dist/index.d.ts +92 -24
- package/dist/index.js +338 -54
- package/dist/index.js.map +1 -1
- package/dist/spark.d.ts +24 -3
- package/dist/spark.js +91 -6
- package/dist/spark.js.map +1 -1
- package/dist/{types-FFW7ykBu.d.ts → types-izZlUweI.d.ts} +109 -16
- package/package.json +1 -1
- package/src/components/Body.tsx +3 -1
- package/src/components/DragInteraction.tsx +1 -1
- package/src/components/IkGizmo.tsx +2 -2
- package/src/components/SceneRenderer.tsx +1 -1
- package/src/components/TrajectoryPlayer.tsx +4 -1
- package/src/components/VisualScenario.tsx +343 -6
- package/src/core/MujocoCanvas.tsx +8 -1
- package/src/core/MujocoPhysics.tsx +10 -4
- package/src/core/MujocoSimProvider.tsx +15 -12
- package/src/core/SceneLoader.ts +182 -3
- package/src/core/createController.tsx +2 -2
- package/src/hooks/useBodyState.ts +1 -1
- package/src/hooks/useContacts.ts +1 -1
- package/src/hooks/useCtrlNoise.ts +1 -1
- package/src/hooks/useFrameCapture.ts +206 -0
- package/src/hooks/useGamepad.ts +1 -1
- package/src/hooks/useGravityCompensation.ts +1 -1
- package/src/hooks/useIkController.ts +22 -13
- package/src/hooks/useJointState.ts +1 -1
- package/src/hooks/useKeyboardTeleop.ts +1 -1
- package/src/hooks/usePolicy.ts +13 -9
- package/src/hooks/useSensor.ts +1 -1
- package/src/hooks/useTrajectoryPlayer.ts +4 -4
- package/src/hooks/useTrajectoryRecorder.ts +1 -1
- package/src/index.ts +35 -0
- package/src/spark.tsx +138 -4
- package/src/types.ts +128 -21
- package/dist/chunk-KGFRKPLS.js +0 -186
- package/dist/chunk-KGFRKPLS.js.map +0 -1
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import { useCallback, useRef } from 'react';
|
|
9
9
|
import { useFrame } from '@react-three/fiber';
|
|
10
10
|
import { useMujocoContext, useBeforePhysicsStep } from '../core/MujocoSimProvider';
|
|
11
|
-
import type { PlaybackState, TrajectoryFrame, TrajectoryInput } from '../types';
|
|
11
|
+
import type { PlaybackState, TrajectoryStateChangeInput, TrajectoryFrame, TrajectoryInput } from '../types';
|
|
12
12
|
|
|
13
13
|
export interface TrajectoryPlayerOptions {
|
|
14
14
|
fps?: number;
|
|
@@ -16,7 +16,7 @@ export interface TrajectoryPlayerOptions {
|
|
|
16
16
|
loop?: boolean;
|
|
17
17
|
mode?: 'kinematic' | 'physics';
|
|
18
18
|
onComplete?: () => void;
|
|
19
|
-
onStateChange?: (
|
|
19
|
+
onStateChange?: (input: TrajectoryStateChangeInput) => void;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
/** Check if input is TrajectoryFrame[] (vs number[][]) */
|
|
@@ -74,7 +74,7 @@ export function useTrajectoryPlayer(
|
|
|
74
74
|
const setState = useCallback((next: PlaybackState) => {
|
|
75
75
|
if (stateRef.current === next) return;
|
|
76
76
|
stateRef.current = next;
|
|
77
|
-
optionsRef.current.onStateChange?.(next);
|
|
77
|
+
optionsRef.current.onStateChange?.({ state: next });
|
|
78
78
|
}, []);
|
|
79
79
|
|
|
80
80
|
const play = useCallback(() => {
|
|
@@ -181,7 +181,7 @@ export function useTrajectoryPlayer(
|
|
|
181
181
|
});
|
|
182
182
|
|
|
183
183
|
// --- Physics mode: set ctrl values each physics step ---
|
|
184
|
-
useBeforePhysicsStep((model, data) => {
|
|
184
|
+
useBeforePhysicsStep(({ model, data }) => {
|
|
185
185
|
if (stateRef.current !== 'playing') return;
|
|
186
186
|
if ((optionsRef.current.mode ?? 'kinematic') !== 'physics') return;
|
|
187
187
|
|
|
@@ -22,7 +22,7 @@ export function useTrajectoryRecorder(options: RecorderOptions = {}) {
|
|
|
22
22
|
const framesRef = useRef<TrajectoryFrame[]>([]);
|
|
23
23
|
const fields = options.fields ?? ['qpos'];
|
|
24
24
|
|
|
25
|
-
useAfterPhysicsStep((
|
|
25
|
+
useAfterPhysicsStep(({ data }) => {
|
|
26
26
|
if (!recordingRef.current) return;
|
|
27
27
|
|
|
28
28
|
const frame: TrajectoryFrame = {
|
package/src/index.ts
CHANGED
|
@@ -44,11 +44,15 @@ export { SceneLights } from './components/SceneLights';
|
|
|
44
44
|
export {
|
|
45
45
|
ScenarioLighting,
|
|
46
46
|
SplatEnvironment,
|
|
47
|
+
VisualScenarioEffects,
|
|
48
|
+
createPairedSplatEnvironment,
|
|
47
49
|
createSparkSplatViewerUrl,
|
|
48
50
|
createSplatEnvironmentUserData,
|
|
49
51
|
getScenarioBackground,
|
|
50
52
|
getScenarioCameraPosition,
|
|
51
53
|
useSplatEnvironment,
|
|
54
|
+
useVisualScenarioEffects,
|
|
55
|
+
withSplatEnvironment,
|
|
52
56
|
} from './components/VisualScenario';
|
|
53
57
|
export { Debug } from './components/Debug';
|
|
54
58
|
export { TendonRenderer } from './components/TendonRenderer';
|
|
@@ -73,6 +77,20 @@ export { useTrajectoryPlayer } from './hooks/useTrajectoryPlayer';
|
|
|
73
77
|
export { useTrajectoryRecorder } from './hooks/useTrajectoryRecorder';
|
|
74
78
|
export { useGamepad } from './hooks/useGamepad';
|
|
75
79
|
export { useVideoRecorder } from './hooks/useVideoRecorder';
|
|
80
|
+
export {
|
|
81
|
+
captureFrame,
|
|
82
|
+
captureFrameBlob,
|
|
83
|
+
useFrameCapture,
|
|
84
|
+
} from './hooks/useFrameCapture';
|
|
85
|
+
export type {
|
|
86
|
+
FrameCaptureAPI,
|
|
87
|
+
FrameCaptureBlobResult,
|
|
88
|
+
FrameCaptureOptions,
|
|
89
|
+
FrameCaptureResult,
|
|
90
|
+
FrameCaptureStatus,
|
|
91
|
+
FrameCaptureTarget,
|
|
92
|
+
FrameCaptureTargetRef,
|
|
93
|
+
} from './hooks/useFrameCapture';
|
|
76
94
|
export { useCtrlNoise } from './hooks/useCtrlNoise';
|
|
77
95
|
export { useBodyMeshes } from './hooks/useBodyMeshes';
|
|
78
96
|
export { useSelectionHighlight } from './hooks/useSelectionHighlight';
|
|
@@ -92,8 +110,14 @@ export type {
|
|
|
92
110
|
IkConfig,
|
|
93
111
|
IkContextValue,
|
|
94
112
|
IKSolveFn,
|
|
113
|
+
IkSolveInput,
|
|
95
114
|
// Callbacks
|
|
96
115
|
PhysicsStepCallback,
|
|
116
|
+
PhysicsStepInput,
|
|
117
|
+
ResetCallbackInput,
|
|
118
|
+
ReadyCallbackInput,
|
|
119
|
+
StepCallbackInput,
|
|
120
|
+
SelectionCallbackInput,
|
|
97
121
|
// State management
|
|
98
122
|
StateSnapshot,
|
|
99
123
|
// Model introspection
|
|
@@ -124,6 +148,10 @@ export type {
|
|
|
124
148
|
KeyboardTeleopConfig,
|
|
125
149
|
// Policy
|
|
126
150
|
PolicyConfig,
|
|
151
|
+
PolicyVector,
|
|
152
|
+
PolicyObservationInput,
|
|
153
|
+
PolicyInferenceInput,
|
|
154
|
+
PolicyActionInput,
|
|
127
155
|
// Observations
|
|
128
156
|
ObservationConfig,
|
|
129
157
|
ObservationHandle,
|
|
@@ -133,6 +161,7 @@ export type {
|
|
|
133
161
|
// Component props
|
|
134
162
|
BodyProps,
|
|
135
163
|
IkGizmoProps,
|
|
164
|
+
IkGizmoDragInput,
|
|
136
165
|
DragInteractionProps,
|
|
137
166
|
DebugProps,
|
|
138
167
|
SceneLightsProps,
|
|
@@ -147,10 +176,16 @@ export type {
|
|
|
147
176
|
PairedSplatEnvironmentConfig,
|
|
148
177
|
SplatEnvironmentMetadataInput,
|
|
149
178
|
SplatEnvironmentMetadata,
|
|
179
|
+
SplatSceneInput,
|
|
150
180
|
VisualScenarioConfig,
|
|
151
181
|
ScenarioLightingProps,
|
|
182
|
+
ScenarioMaterialConfig,
|
|
152
183
|
SplatEnvironmentProps,
|
|
184
|
+
VisualScenarioEffectsProps,
|
|
185
|
+
VisualScenarioMaterialFilterInput,
|
|
153
186
|
TrajectoryPlayerProps,
|
|
187
|
+
TrajectoryFrameCallbackInput,
|
|
188
|
+
TrajectoryStateChangeInput,
|
|
154
189
|
ContactListenerProps,
|
|
155
190
|
// API
|
|
156
191
|
MujocoSimAPI,
|
package/src/spark.tsx
CHANGED
|
@@ -4,7 +4,13 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { useThree } from '@react-three/fiber';
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
useCallback,
|
|
9
|
+
useEffect,
|
|
10
|
+
useMemo,
|
|
11
|
+
useRef,
|
|
12
|
+
useState,
|
|
13
|
+
} from 'react';
|
|
8
14
|
import * as THREE from 'three';
|
|
9
15
|
import {
|
|
10
16
|
SplatEnvironment,
|
|
@@ -17,9 +23,22 @@ import type {
|
|
|
17
23
|
type SparkModule = typeof import('@sparkjsdev/spark');
|
|
18
24
|
type SparkRendererInstance = InstanceType<SparkModule['SparkRenderer']>;
|
|
19
25
|
type SparkSplatMeshInstance = InstanceType<SparkModule['SplatMesh']>;
|
|
26
|
+
type SparkDisposable = {
|
|
27
|
+
dispose?: () => unknown;
|
|
28
|
+
};
|
|
20
29
|
|
|
21
30
|
export type SparkSplatStatus = 'idle' | 'loading' | 'ready' | 'error';
|
|
22
31
|
|
|
32
|
+
export interface SparkSplatLifecycle {
|
|
33
|
+
status: SparkSplatStatus;
|
|
34
|
+
error: Error | null;
|
|
35
|
+
isLoading: boolean;
|
|
36
|
+
isReady: boolean;
|
|
37
|
+
isError: boolean;
|
|
38
|
+
props: Pick<SparkSplatEnvironmentProps, 'onStatusChange' | 'onError'>;
|
|
39
|
+
reset: () => void;
|
|
40
|
+
}
|
|
41
|
+
|
|
23
42
|
export interface SparkSplatEnvironmentProps extends SplatEnvironmentProps {
|
|
24
43
|
/** Enable Spark LoD handling for large splat assets. Default: true. */
|
|
25
44
|
lod?: boolean | 'quality';
|
|
@@ -34,6 +53,75 @@ export interface SparkSplatEnvironmentProps extends SplatEnvironmentProps {
|
|
|
34
53
|
onError?: (error: Error) => void;
|
|
35
54
|
}
|
|
36
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Tracks Spark 3DGS loading state for UI that wraps `SparkSplatEnvironment`.
|
|
58
|
+
*
|
|
59
|
+
* Use the returned `props` with `<SparkSplatEnvironment {...lifecycle.props} />`
|
|
60
|
+
* to avoid repeating status/error state in app code.
|
|
61
|
+
*/
|
|
62
|
+
export function useSparkSplatLifecycle({
|
|
63
|
+
enabled = true,
|
|
64
|
+
initialStatus,
|
|
65
|
+
onError,
|
|
66
|
+
onStatusChange,
|
|
67
|
+
}: {
|
|
68
|
+
enabled?: boolean;
|
|
69
|
+
initialStatus?: SparkSplatStatus;
|
|
70
|
+
onError?: (error: Error) => void;
|
|
71
|
+
onStatusChange?: (status: SparkSplatStatus) => void;
|
|
72
|
+
} = {}): SparkSplatLifecycle {
|
|
73
|
+
const [status, setStatus] = useState<SparkSplatStatus>(
|
|
74
|
+
initialStatus ?? (enabled ? 'loading' : 'idle')
|
|
75
|
+
);
|
|
76
|
+
const [error, setError] = useState<Error | null>(null);
|
|
77
|
+
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
setStatus(enabled ? initialStatus ?? 'loading' : 'idle');
|
|
80
|
+
setError(null);
|
|
81
|
+
}, [enabled, initialStatus]);
|
|
82
|
+
|
|
83
|
+
const handleStatusChange = useCallback(
|
|
84
|
+
(nextStatus: SparkSplatStatus) => {
|
|
85
|
+
setStatus(nextStatus);
|
|
86
|
+
if (nextStatus !== 'error') {
|
|
87
|
+
setError(null);
|
|
88
|
+
}
|
|
89
|
+
onStatusChange?.(nextStatus);
|
|
90
|
+
},
|
|
91
|
+
[onStatusChange]
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
const handleError = useCallback(
|
|
95
|
+
(nextError: Error) => {
|
|
96
|
+
setError(nextError);
|
|
97
|
+
setStatus('error');
|
|
98
|
+
onError?.(nextError);
|
|
99
|
+
},
|
|
100
|
+
[onError]
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
const reset = useCallback(() => {
|
|
104
|
+
setStatus(enabled ? initialStatus ?? 'loading' : 'idle');
|
|
105
|
+
setError(null);
|
|
106
|
+
}, [enabled, initialStatus]);
|
|
107
|
+
|
|
108
|
+
return useMemo(
|
|
109
|
+
() => ({
|
|
110
|
+
status,
|
|
111
|
+
error,
|
|
112
|
+
isLoading: status === 'loading',
|
|
113
|
+
isReady: status === 'ready',
|
|
114
|
+
isError: status === 'error',
|
|
115
|
+
props: {
|
|
116
|
+
onStatusChange: handleStatusChange,
|
|
117
|
+
onError: handleError,
|
|
118
|
+
},
|
|
119
|
+
reset,
|
|
120
|
+
}),
|
|
121
|
+
[error, handleError, handleStatusChange, reset, status]
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
37
125
|
/**
|
|
38
126
|
* Optional SparkJS-backed Gaussian splat renderer for React Three Fiber scenes.
|
|
39
127
|
*
|
|
@@ -42,6 +130,8 @@ export interface SparkSplatEnvironmentProps extends SplatEnvironmentProps {
|
|
|
42
130
|
*/
|
|
43
131
|
export function SparkSplatEnvironment({
|
|
44
132
|
environment,
|
|
133
|
+
scenario,
|
|
134
|
+
renderer = 'spark',
|
|
45
135
|
src,
|
|
46
136
|
format,
|
|
47
137
|
collisionProxy,
|
|
@@ -66,6 +156,8 @@ export function SparkSplatEnvironment({
|
|
|
66
156
|
const { gl, invalidate } = useThree();
|
|
67
157
|
const metadata = useSplatEnvironment({
|
|
68
158
|
environment,
|
|
159
|
+
scenario,
|
|
160
|
+
renderer,
|
|
69
161
|
src,
|
|
70
162
|
format,
|
|
71
163
|
collisionProxy: collisionProxyMetadata,
|
|
@@ -99,11 +191,20 @@ export function SparkSplatEnvironment({
|
|
|
99
191
|
}
|
|
100
192
|
|
|
101
193
|
async function loadSplat() {
|
|
102
|
-
if (!metadata.src
|
|
194
|
+
if (!metadata.src) {
|
|
103
195
|
setLifecycleStatus('idle');
|
|
104
196
|
return;
|
|
105
197
|
}
|
|
106
198
|
|
|
199
|
+
if (metadata.format !== 'spz') {
|
|
200
|
+
const unsupportedFormatError = new Error(
|
|
201
|
+
`SparkSplatEnvironment only supports .spz assets; received "${metadata.format}".`
|
|
202
|
+
);
|
|
203
|
+
setLifecycleStatus('error');
|
|
204
|
+
onErrorRef.current?.(unsupportedFormatError);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
|
|
107
208
|
setLifecycleStatus('loading');
|
|
108
209
|
|
|
109
210
|
try {
|
|
@@ -166,13 +267,13 @@ export function SparkSplatEnvironment({
|
|
|
166
267
|
|
|
167
268
|
if (meshRef.current) {
|
|
168
269
|
groupRef.current?.remove(meshRef.current);
|
|
169
|
-
meshRef.current
|
|
270
|
+
safelyDisposeSparkResource(meshRef.current);
|
|
170
271
|
meshRef.current = null;
|
|
171
272
|
}
|
|
172
273
|
|
|
173
274
|
if (sparkRef.current) {
|
|
174
275
|
groupRef.current?.remove(sparkRef.current);
|
|
175
|
-
sparkRef.current
|
|
276
|
+
safelyDisposeSparkResource(sparkRef.current);
|
|
176
277
|
sparkRef.current = null;
|
|
177
278
|
}
|
|
178
279
|
};
|
|
@@ -189,6 +290,8 @@ export function SparkSplatEnvironment({
|
|
|
189
290
|
<SplatEnvironment
|
|
190
291
|
{...groupProps}
|
|
191
292
|
environment={environment}
|
|
293
|
+
scenario={scenario}
|
|
294
|
+
renderer={renderer}
|
|
192
295
|
src={metadata.src}
|
|
193
296
|
format={metadata.format}
|
|
194
297
|
collisionProxyMetadata={metadata.collisionProxy}
|
|
@@ -200,3 +303,34 @@ export function SparkSplatEnvironment({
|
|
|
200
303
|
</SplatEnvironment>
|
|
201
304
|
);
|
|
202
305
|
}
|
|
306
|
+
|
|
307
|
+
function safelyDisposeSparkResource(resource: SparkDisposable) {
|
|
308
|
+
try {
|
|
309
|
+
const result = resource.dispose?.();
|
|
310
|
+
if (isPromiseLike(result)) {
|
|
311
|
+
void Promise.resolve(result).catch(handleSparkDisposeError);
|
|
312
|
+
}
|
|
313
|
+
} catch (error) {
|
|
314
|
+
handleSparkDisposeError(error);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function isPromiseLike(value: unknown): value is PromiseLike<unknown> {
|
|
319
|
+
return (
|
|
320
|
+
typeof value === 'object' &&
|
|
321
|
+
value !== null &&
|
|
322
|
+
'then' in value &&
|
|
323
|
+
typeof (value as { then?: unknown }).then === 'function'
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function handleSparkDisposeError(error: unknown) {
|
|
328
|
+
if (
|
|
329
|
+
error instanceof Error &&
|
|
330
|
+
error.message.toLowerCase().includes('worker terminate')
|
|
331
|
+
) {
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
console.warn('[mujoco-react] Spark resource disposal failed.', error);
|
|
336
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -429,6 +429,8 @@ export interface SceneObject {
|
|
|
429
429
|
solref?: string;
|
|
430
430
|
solimp?: string;
|
|
431
431
|
condim?: number;
|
|
432
|
+
/** MuJoCo geom group. Group 3 is conventionally used for collision-only helper geoms. */
|
|
433
|
+
group?: number;
|
|
432
434
|
}
|
|
433
435
|
|
|
434
436
|
export interface XmlPatch {
|
|
@@ -443,10 +445,12 @@ export type LocalMujocoFile = File;
|
|
|
443
445
|
export interface LoadFromFilesOptions {
|
|
444
446
|
/** Entry MJCF/URDF file. Inferred from scene.xml, model.xml, robot.xml, or the first XML/URDF file when omitted. */
|
|
445
447
|
sceneFile?: string;
|
|
448
|
+
/** Additional MJCF environment XML files merged into the entry scene before MuJoCo compilation. */
|
|
449
|
+
environmentFiles?: string[];
|
|
446
450
|
homeJoints?: number[];
|
|
447
451
|
xmlPatches?: XmlPatch[];
|
|
448
452
|
sceneObjects?: SceneObject[];
|
|
449
|
-
onReset?: (
|
|
453
|
+
onReset?: (input: ResetCallbackInput) => void;
|
|
450
454
|
}
|
|
451
455
|
|
|
452
456
|
export interface SceneConfig {
|
|
@@ -456,10 +460,18 @@ export interface SceneConfig {
|
|
|
456
460
|
sceneFile: string;
|
|
457
461
|
/** Browser-selected files for local MJCF/URDF loading. Preserves webkitRelativePath when available. */
|
|
458
462
|
files?: readonly LocalMujocoFile[];
|
|
463
|
+
/**
|
|
464
|
+
* Additional MJCF environment XML files merged into the entry scene before compilation.
|
|
465
|
+
*
|
|
466
|
+
* Use this for static collision/physics layers such as a Gaussian-splat
|
|
467
|
+
* environment's proxy `scene.xml`; render the splat itself as a separate
|
|
468
|
+
* visual layer.
|
|
469
|
+
*/
|
|
470
|
+
environmentFiles?: string[];
|
|
459
471
|
sceneObjects?: SceneObject[];
|
|
460
472
|
homeJoints?: number[];
|
|
461
473
|
xmlPatches?: XmlPatch[];
|
|
462
|
-
onReset?: (
|
|
474
|
+
onReset?: (input: ResetCallbackInput) => void;
|
|
463
475
|
}
|
|
464
476
|
|
|
465
477
|
// ---- IK Controller Config ----
|
|
@@ -501,7 +513,7 @@ export interface IkContextValue {
|
|
|
501
513
|
setIkEnabled: (enabled: boolean) => void;
|
|
502
514
|
moveTarget: (pos: THREE.Vector3, duration?: number) => void;
|
|
503
515
|
syncTargetToSite: () => void;
|
|
504
|
-
solveIK: (
|
|
516
|
+
solveIK: (input: IkSolveInput) => number[] | null;
|
|
505
517
|
getGizmoStats: () => { pos: THREE.Vector3; rot: THREE.Euler } | null;
|
|
506
518
|
}
|
|
507
519
|
|
|
@@ -524,12 +536,16 @@ export interface PhysicsConfig {
|
|
|
524
536
|
// ---- IK ----
|
|
525
537
|
|
|
526
538
|
export type IKSolveFn = (
|
|
527
|
-
|
|
528
|
-
quat: THREE.Quaternion,
|
|
529
|
-
currentQ: number[],
|
|
530
|
-
context?: IKSolveContext
|
|
539
|
+
input: IkSolveInput
|
|
531
540
|
) => number[] | null;
|
|
532
541
|
|
|
542
|
+
export interface IkSolveInput {
|
|
543
|
+
position: THREE.Vector3;
|
|
544
|
+
quaternion: THREE.Quaternion;
|
|
545
|
+
currentQ: number[];
|
|
546
|
+
context?: IKSolveContext;
|
|
547
|
+
}
|
|
548
|
+
|
|
533
549
|
export interface IKSolveContext {
|
|
534
550
|
model: MujocoModel;
|
|
535
551
|
data: MujocoData;
|
|
@@ -539,10 +555,29 @@ export interface IKSolveContext {
|
|
|
539
555
|
|
|
540
556
|
// ---- Callbacks ----
|
|
541
557
|
|
|
542
|
-
export
|
|
543
|
-
model: MujocoModel
|
|
544
|
-
data: MujocoData
|
|
545
|
-
|
|
558
|
+
export interface PhysicsStepInput {
|
|
559
|
+
model: MujocoModel;
|
|
560
|
+
data: MujocoData;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
export interface ResetCallbackInput extends PhysicsStepInput {}
|
|
564
|
+
|
|
565
|
+
export interface ReadyCallbackInput {
|
|
566
|
+
api: MujocoSimAPI;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
export interface StepCallbackInput {
|
|
570
|
+
time: number;
|
|
571
|
+
model: MujocoModel;
|
|
572
|
+
data: MujocoData;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
export interface SelectionCallbackInput {
|
|
576
|
+
bodyId: number;
|
|
577
|
+
name: string;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
export type PhysicsStepCallback = (input: PhysicsStepInput) => void;
|
|
546
581
|
|
|
547
582
|
// ---- State Management (spec 4.1) ----
|
|
548
583
|
|
|
@@ -709,10 +744,28 @@ export interface KeyboardTeleopConfig {
|
|
|
709
744
|
|
|
710
745
|
// ---- Policy (spec 10.1) ----
|
|
711
746
|
|
|
747
|
+
export type PolicyVector = Float32Array | Float64Array | number[];
|
|
748
|
+
|
|
749
|
+
export interface PolicyObservationInput {
|
|
750
|
+
model: MujocoModel;
|
|
751
|
+
data: MujocoData;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
export interface PolicyInferenceInput extends PolicyObservationInput {
|
|
755
|
+
observation: PolicyVector;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
export interface PolicyActionInput extends PolicyInferenceInput {
|
|
759
|
+
action: PolicyVector;
|
|
760
|
+
}
|
|
761
|
+
|
|
712
762
|
export interface PolicyConfig {
|
|
713
763
|
frequency: number;
|
|
714
|
-
|
|
715
|
-
|
|
764
|
+
enabled?: boolean;
|
|
765
|
+
onObservation: (input: PolicyObservationInput) => PolicyVector;
|
|
766
|
+
/** Run policy inference. Omit to pass observations directly to `onAction` for custom inline controllers. */
|
|
767
|
+
infer?: (input: PolicyInferenceInput) => PolicyVector;
|
|
768
|
+
onAction: (input: PolicyActionInput) => void;
|
|
716
769
|
}
|
|
717
770
|
|
|
718
771
|
// ---- Observation Builder ----
|
|
@@ -778,7 +831,12 @@ export interface IkGizmoProps {
|
|
|
778
831
|
controller: IkContextValue;
|
|
779
832
|
siteName?: string;
|
|
780
833
|
scale?: number;
|
|
781
|
-
onDrag?: (
|
|
834
|
+
onDrag?: (input: IkGizmoDragInput) => void;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
export interface IkGizmoDragInput {
|
|
838
|
+
position: THREE.Vector3;
|
|
839
|
+
quaternion: THREE.Quaternion;
|
|
782
840
|
}
|
|
783
841
|
|
|
784
842
|
export interface DragInteractionProps {
|
|
@@ -805,6 +863,13 @@ export interface ScenarioCameraConfig {
|
|
|
805
863
|
blur?: number;
|
|
806
864
|
}
|
|
807
865
|
|
|
866
|
+
export interface ScenarioMaterialConfig {
|
|
867
|
+
randomizeObjectColors?: boolean;
|
|
868
|
+
randomizeTableMaterial?: boolean;
|
|
869
|
+
roughness?: number;
|
|
870
|
+
metalness?: number;
|
|
871
|
+
}
|
|
872
|
+
|
|
808
873
|
export interface SplatAssetConfig {
|
|
809
874
|
src: string;
|
|
810
875
|
/** Common browser-friendly splat format. Renderer-specific loaders may accept more. */
|
|
@@ -819,7 +884,7 @@ export interface SplatScenarioConfig {
|
|
|
819
884
|
format?: SplatFormat;
|
|
820
885
|
src?: string;
|
|
821
886
|
requiresCollisionProxy?: boolean;
|
|
822
|
-
collisionProxy?: SplatCollisionProxyConfig;
|
|
887
|
+
collisionProxy?: SplatCollisionProxyConfig | null;
|
|
823
888
|
}
|
|
824
889
|
|
|
825
890
|
export interface SplatCollisionProxyConfig {
|
|
@@ -845,6 +910,8 @@ export interface PairedSplatEnvironmentConfig {
|
|
|
845
910
|
|
|
846
911
|
export interface SplatEnvironmentMetadataInput {
|
|
847
912
|
environment?: PairedSplatEnvironmentConfig;
|
|
913
|
+
scenario?: VisualScenarioConfig;
|
|
914
|
+
renderer?: SplatRendererKind;
|
|
848
915
|
src?: string;
|
|
849
916
|
format?: SplatFormat;
|
|
850
917
|
collisionProxy?: SplatCollisionProxyConfig;
|
|
@@ -857,6 +924,12 @@ export interface SplatEnvironmentMetadata {
|
|
|
857
924
|
userData: Record<string, unknown>;
|
|
858
925
|
}
|
|
859
926
|
|
|
927
|
+
export type SplatSceneInput =
|
|
928
|
+
| PairedSplatEnvironmentConfig
|
|
929
|
+
| VisualScenarioConfig
|
|
930
|
+
| undefined
|
|
931
|
+
| null;
|
|
932
|
+
|
|
860
933
|
export interface VisualScenarioConfig {
|
|
861
934
|
id?: string;
|
|
862
935
|
label?: string;
|
|
@@ -864,7 +937,8 @@ export interface VisualScenarioConfig {
|
|
|
864
937
|
lighting?: ScenarioLightingPreset;
|
|
865
938
|
environment?: string;
|
|
866
939
|
camera?: ScenarioCameraConfig;
|
|
867
|
-
|
|
940
|
+
materials?: ScenarioMaterialConfig;
|
|
941
|
+
splat?: SplatScenarioConfig | null;
|
|
868
942
|
}
|
|
869
943
|
|
|
870
944
|
export interface ScenarioLightingProps {
|
|
@@ -875,6 +949,8 @@ export interface ScenarioLightingProps {
|
|
|
875
949
|
|
|
876
950
|
export interface SplatEnvironmentProps extends Omit<ThreeElements['group'], 'ref'> {
|
|
877
951
|
environment?: PairedSplatEnvironmentConfig;
|
|
952
|
+
scenario?: VisualScenarioConfig;
|
|
953
|
+
renderer?: SplatRendererKind;
|
|
878
954
|
src?: string;
|
|
879
955
|
format?: SplatFormat;
|
|
880
956
|
collisionProxy?: ReactNode;
|
|
@@ -882,6 +958,24 @@ export interface SplatEnvironmentProps extends Omit<ThreeElements['group'], 'ref
|
|
|
882
958
|
showPlaceholder?: boolean;
|
|
883
959
|
}
|
|
884
960
|
|
|
961
|
+
export interface VisualScenarioEffectsProps {
|
|
962
|
+
scenario?: VisualScenarioConfig;
|
|
963
|
+
enabled?: boolean;
|
|
964
|
+
applyBackground?: boolean;
|
|
965
|
+
applyFog?: boolean;
|
|
966
|
+
applyRenderer?: boolean;
|
|
967
|
+
applyMaterials?: boolean;
|
|
968
|
+
background?: THREE.ColorRepresentation;
|
|
969
|
+
fogNear?: number;
|
|
970
|
+
fogFar?: number;
|
|
971
|
+
materialFilter?: (input: VisualScenarioMaterialFilterInput) => boolean;
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
export interface VisualScenarioMaterialFilterInput {
|
|
975
|
+
object: THREE.Object3D;
|
|
976
|
+
material: THREE.Material;
|
|
977
|
+
}
|
|
978
|
+
|
|
885
979
|
export type TrajectoryInput = TrajectoryFrame[] | number[][];
|
|
886
980
|
|
|
887
981
|
export interface TrajectoryPlayerProps {
|
|
@@ -891,9 +985,18 @@ export interface TrajectoryPlayerProps {
|
|
|
891
985
|
loop?: boolean;
|
|
892
986
|
playing?: boolean;
|
|
893
987
|
mode?: 'kinematic' | 'physics';
|
|
894
|
-
onFrame?: (
|
|
988
|
+
onFrame?: (input: TrajectoryFrameCallbackInput) => void;
|
|
895
989
|
onComplete?: () => void;
|
|
896
|
-
onStateChange?: (
|
|
990
|
+
onStateChange?: (input: TrajectoryStateChangeInput) => void;
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
export interface TrajectoryFrameCallbackInput {
|
|
994
|
+
frameIndex: number;
|
|
995
|
+
frame: TrajectoryFrame | number[] | undefined;
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
export interface TrajectoryStateChangeInput {
|
|
999
|
+
state: PlaybackState;
|
|
897
1000
|
}
|
|
898
1001
|
|
|
899
1002
|
export interface SelectionHighlightProps {
|
|
@@ -920,6 +1023,8 @@ export interface BodyProps {
|
|
|
920
1023
|
solref?: string;
|
|
921
1024
|
solimp?: string;
|
|
922
1025
|
condim?: number;
|
|
1026
|
+
/** MuJoCo geom group. Group 3 is conventionally used for collision-only helper geoms. */
|
|
1027
|
+
group?: number;
|
|
923
1028
|
children?: ReactNode;
|
|
924
1029
|
}
|
|
925
1030
|
|
|
@@ -1017,10 +1122,12 @@ export interface MujocoSimAPI {
|
|
|
1017
1122
|
|
|
1018
1123
|
export type MujocoCanvasProps = Omit<CanvasProps, 'onError'> & {
|
|
1019
1124
|
config: SceneConfig;
|
|
1020
|
-
|
|
1125
|
+
/** R3F content rendered while the MuJoCo WASM module is still loading. */
|
|
1126
|
+
loadingFallback?: ReactNode;
|
|
1127
|
+
onReady?: (input: ReadyCallbackInput) => void;
|
|
1021
1128
|
onError?: (error: Error) => void;
|
|
1022
|
-
onStep?: (
|
|
1023
|
-
onSelection?: (
|
|
1129
|
+
onStep?: (input: StepCallbackInput) => void;
|
|
1130
|
+
onSelection?: (input: SelectionCallbackInput) => void;
|
|
1024
1131
|
// Declarative physics config (spec 1.1)
|
|
1025
1132
|
gravity?: [number, number, number];
|
|
1026
1133
|
timestep?: number;
|