mujoco-react 10.4.0 → 10.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +73 -136
- package/dist/{chunk-FBXXXPLQ.js → chunk-EN55TTGH.js} +157 -16
- package/dist/chunk-EN55TTGH.js.map +1 -0
- package/dist/index.d.ts +179 -48
- package/dist/index.js +487 -20
- package/dist/index.js.map +1 -1
- package/dist/onnx.d.ts +65 -0
- package/dist/onnx.js +58 -0
- package/dist/onnx.js.map +1 -0
- package/dist/spark.d.ts +1 -1
- package/dist/spark.js +1 -1
- package/dist/{types-CdFZCYmy.d.ts → types-Dvtm4I0o.d.ts} +155 -4
- package/package.json +14 -3
- package/src/components/CameraView.tsx +245 -0
- package/src/core/GenericIK.ts +22 -3
- package/src/core/MujocoSimProvider.tsx +37 -1
- package/src/core/SceneLoader.ts +3 -2
- package/src/hooks/useCameraStream.ts +115 -0
- package/src/hooks/useControlGroup.ts +0 -0
- package/src/hooks/useIkController.ts +36 -5
- package/src/hooks/usePolicyCameraTensors.ts +215 -0
- package/src/index.ts +44 -0
- package/src/onnx.ts +126 -0
- package/src/policyImageTensors.ts +150 -0
- package/src/rendering/cameraFrameCapture.ts +112 -15
- package/src/types.ts +50 -3
- package/dist/chunk-FBXXXPLQ.js.map +0 -1
|
@@ -13,6 +13,29 @@ import type {
|
|
|
13
13
|
CameraFrameCaptureSource,
|
|
14
14
|
CameraFrameCaptureVector3,
|
|
15
15
|
} from '../types';
|
|
16
|
+
import {
|
|
17
|
+
pixelsToPolicyImageTensor,
|
|
18
|
+
type PolicyImageTensorOptions,
|
|
19
|
+
type PolicyImageTensorResult,
|
|
20
|
+
} from '../policyImageTensors';
|
|
21
|
+
|
|
22
|
+
/** Options for capturing a camera frame straight into a policy image tensor. */
|
|
23
|
+
export type CameraFrameCaptureTensorOptions = CameraFrameCaptureOptions &
|
|
24
|
+
Pick<PolicyImageTensorOptions, 'channels' | 'layout' | 'range'>;
|
|
25
|
+
|
|
26
|
+
export interface CameraFramePixelsResult {
|
|
27
|
+
/** Raw RGBA pixels, bottom-left origin (reused buffer — consume before the next capture). */
|
|
28
|
+
pixels: Uint8Array;
|
|
29
|
+
camera: THREE.Camera;
|
|
30
|
+
width: number;
|
|
31
|
+
height: number;
|
|
32
|
+
source: CameraFrameCaptureSource;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface CameraFrameTensorResult extends PolicyImageTensorResult {
|
|
36
|
+
camera: THREE.Camera;
|
|
37
|
+
source: CameraFrameCaptureSource;
|
|
38
|
+
}
|
|
16
39
|
|
|
17
40
|
export interface CameraFrameCaptureSession {
|
|
18
41
|
readonly width: number;
|
|
@@ -36,6 +59,14 @@ export interface CameraFrameCaptureSession {
|
|
|
36
59
|
options?: CameraFrameCaptureOptions
|
|
37
60
|
): Promise<CameraFrameCaptureResult>;
|
|
38
61
|
captureBlob(options?: CameraFrameCaptureOptions): Promise<CameraFrameCaptureBlobResult>;
|
|
62
|
+
/**
|
|
63
|
+
* Render and read raw RGBA pixels without any canvas/PNG round-trip. The
|
|
64
|
+
* returned buffer is reused between calls — copy or convert it before the
|
|
65
|
+
* next capture.
|
|
66
|
+
*/
|
|
67
|
+
capturePixels(options?: CameraFrameCaptureOptions): CameraFramePixelsResult;
|
|
68
|
+
/** Render straight into a normalized policy image tensor (no canvas/PNG encode). */
|
|
69
|
+
captureTensor(options?: CameraFrameCaptureTensorOptions): CameraFrameTensorResult;
|
|
39
70
|
dispose(): void;
|
|
40
71
|
}
|
|
41
72
|
|
|
@@ -250,7 +281,7 @@ function applyProjectionMatrix(
|
|
|
250
281
|
camera.projectionMatrixInverse.copy(camera.projectionMatrix).invert();
|
|
251
282
|
}
|
|
252
283
|
|
|
253
|
-
function createCaptureCamera(
|
|
284
|
+
export function createCaptureCamera(
|
|
254
285
|
options: CameraFrameCaptureOptions,
|
|
255
286
|
fallbackCamera: THREE.Camera,
|
|
256
287
|
width: number,
|
|
@@ -290,7 +321,7 @@ function getCaptureDimensions(
|
|
|
290
321
|
return { width, height };
|
|
291
322
|
}
|
|
292
323
|
|
|
293
|
-
function prepareCaptureCamera(
|
|
324
|
+
export function prepareCaptureCamera(
|
|
294
325
|
camera: THREE.Camera,
|
|
295
326
|
options: CameraFrameCaptureOptions,
|
|
296
327
|
fallbackCamera: THREE.Camera,
|
|
@@ -646,7 +677,10 @@ export function createCameraFrameCaptureSession(
|
|
|
646
677
|
return captureOptions;
|
|
647
678
|
}
|
|
648
679
|
|
|
649
|
-
function
|
|
680
|
+
function renderCaptureToTarget(
|
|
681
|
+
captureOptions: CameraFrameCaptureOptions,
|
|
682
|
+
readback: () => void
|
|
683
|
+
) {
|
|
650
684
|
const previousState = saveRendererState(sessionRenderer);
|
|
651
685
|
const previousSceneState = applyCaptureVisualOverrides(
|
|
652
686
|
sessionRenderer,
|
|
@@ -676,6 +710,16 @@ export function createCameraFrameCaptureSession(
|
|
|
676
710
|
}
|
|
677
711
|
sessionRenderer.clear();
|
|
678
712
|
sessionRenderer.render(scene, camera);
|
|
713
|
+
readback();
|
|
714
|
+
} finally {
|
|
715
|
+
restoreObjectVisibility(hidden);
|
|
716
|
+
if (previousSceneState) restoreSceneVisualState(scene, previousSceneState);
|
|
717
|
+
restoreRendererState(sessionRenderer, previousState);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
function renderPreparedCapture(captureOptions: CameraFrameCaptureOptions) {
|
|
722
|
+
renderCaptureToTarget(captureOptions, () => {
|
|
679
723
|
readRenderTargetToCanvas(
|
|
680
724
|
sessionRenderer,
|
|
681
725
|
target,
|
|
@@ -688,24 +732,50 @@ export function createCameraFrameCaptureSession(
|
|
|
688
732
|
sessionRenderer.outputColorSpace,
|
|
689
733
|
captureOptions.flipX ?? false
|
|
690
734
|
);
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
}
|
|
699
|
-
restoreObjectVisibility(hidden);
|
|
700
|
-
if (previousSceneState) restoreSceneVisualState(scene, previousSceneState);
|
|
701
|
-
restoreRendererState(sessionRenderer, previousState);
|
|
702
|
-
}
|
|
735
|
+
});
|
|
736
|
+
return {
|
|
737
|
+
canvas,
|
|
738
|
+
camera,
|
|
739
|
+
width,
|
|
740
|
+
height,
|
|
741
|
+
source: getCameraFrameCaptureSource(captureOptions),
|
|
742
|
+
};
|
|
703
743
|
}
|
|
704
744
|
|
|
705
745
|
function capture(nextOptions: CameraFrameCaptureOptions = {}) {
|
|
706
746
|
return renderPreparedCapture(resolveCaptureOptions(nextOptions));
|
|
707
747
|
}
|
|
708
748
|
|
|
749
|
+
function capturePixels(nextOptions: CameraFrameCaptureOptions = {}): CameraFramePixelsResult {
|
|
750
|
+
const captureOptions = resolveCaptureOptions(nextOptions);
|
|
751
|
+
renderCaptureToTarget(captureOptions, () => {
|
|
752
|
+
sessionRenderer.readRenderTargetPixels(target, 0, 0, width, height, pixels);
|
|
753
|
+
});
|
|
754
|
+
return {
|
|
755
|
+
pixels,
|
|
756
|
+
camera,
|
|
757
|
+
width,
|
|
758
|
+
height,
|
|
759
|
+
source: getCameraFrameCaptureSource(captureOptions),
|
|
760
|
+
};
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
function captureTensor(
|
|
764
|
+
nextOptions: CameraFrameCaptureTensorOptions = {}
|
|
765
|
+
): CameraFrameTensorResult {
|
|
766
|
+
const result = capturePixels(nextOptions);
|
|
767
|
+
const tensor = pixelsToPolicyImageTensor(pixels, {
|
|
768
|
+
width,
|
|
769
|
+
height,
|
|
770
|
+
channels: nextOptions.channels,
|
|
771
|
+
layout: nextOptions.layout,
|
|
772
|
+
range: nextOptions.range,
|
|
773
|
+
sourceOrigin: 'bottom-left',
|
|
774
|
+
flipX: nextOptions.flipX,
|
|
775
|
+
});
|
|
776
|
+
return { ...tensor, camera, source: result.source };
|
|
777
|
+
}
|
|
778
|
+
|
|
709
779
|
async function captureAsync(nextOptions: CameraFrameCaptureOptions = {}) {
|
|
710
780
|
const captureOptions = resolveCaptureOptions(nextOptions);
|
|
711
781
|
runCapturePreRenderHooks(scene);
|
|
@@ -779,6 +849,8 @@ export function createCameraFrameCaptureSession(
|
|
|
779
849
|
height,
|
|
780
850
|
capture,
|
|
781
851
|
captureAsync,
|
|
852
|
+
capturePixels,
|
|
853
|
+
captureTensor,
|
|
782
854
|
captureDataUrl(nextOptions = {}) {
|
|
783
855
|
const type = nextOptions.type ?? options.type ?? 'image/png';
|
|
784
856
|
const result = capture(nextOptions);
|
|
@@ -889,3 +961,28 @@ export async function captureCameraFrameBlob(
|
|
|
889
961
|
session.dispose();
|
|
890
962
|
}
|
|
891
963
|
}
|
|
964
|
+
|
|
965
|
+
/**
|
|
966
|
+
* One-shot camera frame capture straight into a policy image tensor, skipping
|
|
967
|
+
* the canvas/PNG round-trip. For repeated captures (live inference, recording),
|
|
968
|
+
* create a session once with {@link createCameraFrameCaptureSession} and call
|
|
969
|
+
* `session.captureTensor()` so the render target and buffers are reused.
|
|
970
|
+
*/
|
|
971
|
+
export function captureCameraFrameTensor(
|
|
972
|
+
renderer: THREE.WebGLRenderer,
|
|
973
|
+
scene: THREE.Scene,
|
|
974
|
+
fallbackCamera: THREE.Camera,
|
|
975
|
+
options: CameraFrameCaptureTensorOptions = {}
|
|
976
|
+
): CameraFrameTensorResult {
|
|
977
|
+
const session = createCameraFrameCaptureSession(
|
|
978
|
+
renderer,
|
|
979
|
+
scene,
|
|
980
|
+
fallbackCamera,
|
|
981
|
+
options
|
|
982
|
+
);
|
|
983
|
+
try {
|
|
984
|
+
return session.captureTensor(options);
|
|
985
|
+
} finally {
|
|
986
|
+
session.dispose();
|
|
987
|
+
}
|
|
988
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -7,6 +7,11 @@ import type React from 'react';
|
|
|
7
7
|
import type { ReactNode } from 'react';
|
|
8
8
|
import type { CanvasProps, ThreeElements } from '@react-three/fiber';
|
|
9
9
|
import * as THREE from 'three';
|
|
10
|
+
import type {
|
|
11
|
+
CameraFrameCaptureSession,
|
|
12
|
+
CameraFrameCaptureTensorOptions,
|
|
13
|
+
CameraFrameTensorResult,
|
|
14
|
+
} from './rendering/cameraFrameCapture';
|
|
10
15
|
|
|
11
16
|
// ---- Register (type-safe named resources) ----
|
|
12
17
|
|
|
@@ -459,6 +464,10 @@ export interface SceneObject {
|
|
|
459
464
|
solref?: string;
|
|
460
465
|
solimp?: string;
|
|
461
466
|
condim?: number;
|
|
467
|
+
/** MuJoCo geom contact type bitmask. Defaults to 1 for generated objects. */
|
|
468
|
+
contype?: number;
|
|
469
|
+
/** MuJoCo geom contact affinity bitmask. Defaults to 1 for generated objects. */
|
|
470
|
+
conaffinity?: number;
|
|
462
471
|
/** MuJoCo geom group. Group 3 is conventionally used for collision-only helper geoms. */
|
|
463
472
|
group?: number;
|
|
464
473
|
}
|
|
@@ -527,6 +536,12 @@ export interface IkConfig {
|
|
|
527
536
|
* starting at index 0. Prefer inferred IK or `joints`/`actuators`.
|
|
528
537
|
*/
|
|
529
538
|
numJoints?: number;
|
|
539
|
+
/**
|
|
540
|
+
* Optional solve-space joint limits in the same order as the resolved joints.
|
|
541
|
+
* Use this when MJCF limits are intentionally broad or when a setup/calibration
|
|
542
|
+
* tool should stay within a narrower envelope.
|
|
543
|
+
*/
|
|
544
|
+
jointLimits?: ReadonlyArray<readonly [number, number] | null | undefined>;
|
|
530
545
|
/** Custom IK solver. When omitted, uses built-in Damped Least-Squares solver. */
|
|
531
546
|
ikSolveFn?: IKSolveFn;
|
|
532
547
|
/** DLS damping. Default: 0.01. */
|
|
@@ -549,7 +564,7 @@ export interface IkContextValue {
|
|
|
549
564
|
ikTargetRef: React.RefObject<THREE.Group>;
|
|
550
565
|
siteIdRef: React.RefObject<number>;
|
|
551
566
|
setIkEnabled: (enabled: boolean) => void;
|
|
552
|
-
moveTarget: (pos:
|
|
567
|
+
moveTarget: (pos: IkTargetPosition, duration?: number) => void;
|
|
553
568
|
syncTargetToSite: () => void;
|
|
554
569
|
solveIK: (input: IkSolveInput) => number[] | null;
|
|
555
570
|
getGizmoStats: () => { pos: THREE.Vector3; rot: THREE.Euler } | null;
|
|
@@ -577,9 +592,28 @@ export type IKSolveFn = (
|
|
|
577
592
|
input: IkSolveInput
|
|
578
593
|
) => number[] | null;
|
|
579
594
|
|
|
595
|
+
export type IkTargetPosition =
|
|
596
|
+
| THREE.Vector3
|
|
597
|
+
| readonly [number, number, number]
|
|
598
|
+
| {
|
|
599
|
+
readonly x: number;
|
|
600
|
+
readonly y: number;
|
|
601
|
+
readonly z: number;
|
|
602
|
+
};
|
|
603
|
+
|
|
604
|
+
export type IkTargetQuaternion =
|
|
605
|
+
| THREE.Quaternion
|
|
606
|
+
| readonly [number, number, number, number]
|
|
607
|
+
| {
|
|
608
|
+
readonly x: number;
|
|
609
|
+
readonly y: number;
|
|
610
|
+
readonly z: number;
|
|
611
|
+
readonly w: number;
|
|
612
|
+
};
|
|
613
|
+
|
|
580
614
|
export interface IkSolveInput {
|
|
581
|
-
position:
|
|
582
|
-
quaternion:
|
|
615
|
+
position: IkTargetPosition;
|
|
616
|
+
quaternion: IkTargetQuaternion;
|
|
583
617
|
currentQ: number[];
|
|
584
618
|
context?: IKSolveContext;
|
|
585
619
|
}
|
|
@@ -1458,6 +1492,19 @@ export interface MujocoSimAPI {
|
|
|
1458
1492
|
captureFrameBlob(options?: MujocoFrameCaptureOptions): Promise<FrameCaptureBlobResult>;
|
|
1459
1493
|
captureCameraFrame(options?: CameraFrameCaptureOptions): Promise<CameraFrameCaptureResult>;
|
|
1460
1494
|
captureCameraFrameBlob(options?: CameraFrameCaptureOptions): Promise<CameraFrameCaptureBlobResult>;
|
|
1495
|
+
/** Capture a camera frame straight into a policy image tensor (no canvas/PNG encode). */
|
|
1496
|
+
captureCameraFrameTensor(options?: CameraFrameCaptureTensorOptions): CameraFrameTensorResult;
|
|
1497
|
+
/**
|
|
1498
|
+
* Create a reusable offscreen capture session bound to this scene. Reuse it
|
|
1499
|
+
* for live inference/recording so the render target and buffers persist
|
|
1500
|
+
* across frames; call `session.captureTensor()` / `capturePixels()` each step.
|
|
1501
|
+
*/
|
|
1502
|
+
createCameraFrameCaptureSession(options?: CameraFrameCaptureOptions): CameraFrameCaptureSession;
|
|
1503
|
+
/**
|
|
1504
|
+
* Resolve a named MuJoCo camera/site/body into concrete capture options with
|
|
1505
|
+
* the current world pose. Useful for re-aiming a persistent session each step.
|
|
1506
|
+
*/
|
|
1507
|
+
resolveCameraCaptureOptions(options?: CameraFrameCaptureOptions): CameraFrameCaptureOptions;
|
|
1461
1508
|
recordCameraSequence(options: CameraFrameSequenceOptions): Promise<CameraFrameSequenceResult>;
|
|
1462
1509
|
project2DTo3D(
|
|
1463
1510
|
x: number,
|