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
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { ScenarioLighting, SplatEnvironment, createSparkSplatViewerUrl, createSplatEnvironmentUserData, getScenarioBackground, getScenarioCameraPosition, useSplatEnvironment } from './chunk-
|
|
1
|
+
export { ScenarioLighting, SplatEnvironment, VisualScenarioEffects, createPairedSplatEnvironment, createSparkSplatViewerUrl, createSplatEnvironmentUserData, getScenarioBackground, getScenarioCameraPosition, useSplatEnvironment, useVisualScenarioEffects, withSplatEnvironment } from './chunk-33CV6HSV.js';
|
|
2
2
|
import loadMujoco from '@mujoco/mujoco';
|
|
3
3
|
import defaultMujocoWasmUrl from '@mujoco/mujoco/mujoco.wasm?url';
|
|
4
4
|
import { createContext, forwardRef, useEffect, useContext, useState, useRef, useCallback, useMemo, useLayoutEffect } from 'react';
|
|
@@ -776,7 +776,8 @@ function sceneObjectToXml(obj) {
|
|
|
776
776
|
const solref = obj.solref ? ` solref="${obj.solref}"` : "";
|
|
777
777
|
const solimp = obj.solimp ? ` solimp="${obj.solimp}"` : "";
|
|
778
778
|
const condim = obj.condim ? ` condim="${obj.condim}"` : "";
|
|
779
|
-
|
|
779
|
+
const group = obj.group !== void 0 ? ` group="${obj.group}"` : "";
|
|
780
|
+
return `<body name="${obj.name}" pos="${pos}">${joint}<geom type="${obj.type}" size="${size}" rgba="${rgba}" contype="1" conaffinity="1"${mass}${friction}${solref}${solimp}${condim}${group}/></body>`;
|
|
780
781
|
}
|
|
781
782
|
function ensureDir(mujoco, fname) {
|
|
782
783
|
const dirParts = fname.split("/");
|
|
@@ -816,6 +817,20 @@ function normalizeVfsPath(path) {
|
|
|
816
817
|
function localFilePath(file) {
|
|
817
818
|
return normalizeVfsPath(file.webkitRelativePath || file.name);
|
|
818
819
|
}
|
|
820
|
+
function dirname(path) {
|
|
821
|
+
const normalized = normalizeVfsPath(path);
|
|
822
|
+
const idx = normalized.lastIndexOf("/");
|
|
823
|
+
return idx === -1 ? "" : normalized.slice(0, idx + 1);
|
|
824
|
+
}
|
|
825
|
+
function relativeVfsPath(fromDir, targetPath) {
|
|
826
|
+
const from = normalizeVfsPath(fromDir).split("/").filter(Boolean);
|
|
827
|
+
const target = normalizeVfsPath(targetPath).split("/").filter(Boolean);
|
|
828
|
+
while (from.length && target.length && from[0] === target[0]) {
|
|
829
|
+
from.shift();
|
|
830
|
+
target.shift();
|
|
831
|
+
}
|
|
832
|
+
return [...from.map(() => ".."), ...target].join("/") || ".";
|
|
833
|
+
}
|
|
819
834
|
function inferSceneFile(files, options) {
|
|
820
835
|
if (options?.sceneFile) return normalizeVfsPath(options.sceneFile);
|
|
821
836
|
const paths = files.map(localFilePath);
|
|
@@ -834,12 +849,120 @@ function createSceneConfigFromFiles(files, options = {}) {
|
|
|
834
849
|
src: "",
|
|
835
850
|
sceneFile: inferSceneFile(fileArray, options),
|
|
836
851
|
files: fileArray,
|
|
852
|
+
environmentFiles: options.environmentFiles?.map(normalizeVfsPath),
|
|
837
853
|
homeJoints: options.homeJoints,
|
|
838
854
|
xmlPatches: options.xmlPatches,
|
|
839
855
|
sceneObjects: options.sceneObjects,
|
|
840
856
|
onReset: options.onReset
|
|
841
857
|
};
|
|
842
858
|
}
|
|
859
|
+
var ENVIRONMENT_MERGE_SECTIONS = [
|
|
860
|
+
"asset",
|
|
861
|
+
"worldbody",
|
|
862
|
+
"contact",
|
|
863
|
+
"equality",
|
|
864
|
+
"tendon",
|
|
865
|
+
"sensor",
|
|
866
|
+
"keyframe",
|
|
867
|
+
"custom",
|
|
868
|
+
"extension"
|
|
869
|
+
];
|
|
870
|
+
function directChild(parent, tagName) {
|
|
871
|
+
const lower = tagName.toLowerCase();
|
|
872
|
+
for (const child of Array.from(parent.children)) {
|
|
873
|
+
if (child.tagName.toLowerCase() === lower) return child;
|
|
874
|
+
}
|
|
875
|
+
return null;
|
|
876
|
+
}
|
|
877
|
+
function ensureTopLevelSection(doc, tagName) {
|
|
878
|
+
const root = doc.documentElement;
|
|
879
|
+
const existing = directChild(root, tagName);
|
|
880
|
+
if (existing) return existing;
|
|
881
|
+
const section = doc.createElement(tagName);
|
|
882
|
+
if (tagName === "asset") {
|
|
883
|
+
const worldbody = directChild(root, "worldbody");
|
|
884
|
+
if (worldbody) root.insertBefore(section, worldbody);
|
|
885
|
+
else root.appendChild(section);
|
|
886
|
+
} else {
|
|
887
|
+
root.appendChild(section);
|
|
888
|
+
}
|
|
889
|
+
return section;
|
|
890
|
+
}
|
|
891
|
+
function readCompilerDirs(doc) {
|
|
892
|
+
const compiler = directChild(doc.documentElement, "compiler");
|
|
893
|
+
const assetDir = compiler?.getAttribute("assetdir") || "";
|
|
894
|
+
return {
|
|
895
|
+
meshDir: compiler?.getAttribute("meshdir") || assetDir,
|
|
896
|
+
textureDir: compiler?.getAttribute("texturedir") || assetDir
|
|
897
|
+
};
|
|
898
|
+
}
|
|
899
|
+
function isExternalPath(path) {
|
|
900
|
+
return /^[a-z]+:\/\//i.test(path) || path.startsWith("package://") || path.startsWith("/");
|
|
901
|
+
}
|
|
902
|
+
function fileReferencePrefix(el, compilerDirs) {
|
|
903
|
+
const tag = el.tagName.toLowerCase();
|
|
904
|
+
if (tag === "mesh") return compilerDirs.meshDir ? compilerDirs.meshDir + "/" : "";
|
|
905
|
+
if (tag === "texture" || tag === "hfield") return compilerDirs.textureDir ? compilerDirs.textureDir + "/" : "";
|
|
906
|
+
return "";
|
|
907
|
+
}
|
|
908
|
+
function rewriteFileReferencesForMerge(node, sourceFile, targetFile, sourceDoc) {
|
|
909
|
+
const sourceDir = dirname(sourceFile);
|
|
910
|
+
const targetDir = dirname(targetFile);
|
|
911
|
+
const compilerDirs = readCompilerDirs(sourceDoc);
|
|
912
|
+
node.querySelectorAll("[file], [filename]").forEach((el) => {
|
|
913
|
+
const attr = el.hasAttribute("file") ? "file" : "filename";
|
|
914
|
+
const value = el.getAttribute(attr);
|
|
915
|
+
if (!value || isExternalPath(value)) return;
|
|
916
|
+
const sourceRelativePath = normalizeVfsPath(fileReferencePrefix(el, compilerDirs) + value);
|
|
917
|
+
const resolvedPath = normalizeVfsPath(sourceDir + sourceRelativePath);
|
|
918
|
+
el.setAttribute(attr, relativeVfsPath(targetDir, resolvedPath));
|
|
919
|
+
});
|
|
920
|
+
}
|
|
921
|
+
function hasParseError(doc) {
|
|
922
|
+
return doc.getElementsByTagName("parsererror").length > 0;
|
|
923
|
+
}
|
|
924
|
+
function composeEnvironmentXml(sceneXml, config, parser, environmentXmlByPath) {
|
|
925
|
+
const environmentFiles = config.environmentFiles?.map(normalizeVfsPath) ?? [];
|
|
926
|
+
if (!environmentFiles.length) return sceneXml;
|
|
927
|
+
const sceneDoc = parser.parseFromString(sceneXml, "text/xml");
|
|
928
|
+
if (hasParseError(sceneDoc)) {
|
|
929
|
+
console.warn(`Could not compose environments: failed to parse ${config.sceneFile}`);
|
|
930
|
+
return sceneXml;
|
|
931
|
+
}
|
|
932
|
+
for (const environmentFile of environmentFiles) {
|
|
933
|
+
const environmentXml = environmentXmlByPath.get(environmentFile);
|
|
934
|
+
if (!environmentXml) {
|
|
935
|
+
console.warn(`Environment XML not found: ${environmentFile}`);
|
|
936
|
+
continue;
|
|
937
|
+
}
|
|
938
|
+
const environmentDoc = parser.parseFromString(environmentXml, "text/xml");
|
|
939
|
+
if (hasParseError(environmentDoc)) {
|
|
940
|
+
console.warn(`Skipping environment XML with parse errors: ${environmentFile}`);
|
|
941
|
+
continue;
|
|
942
|
+
}
|
|
943
|
+
for (const sectionName of ENVIRONMENT_MERGE_SECTIONS) {
|
|
944
|
+
const environmentSection = directChild(environmentDoc.documentElement, sectionName);
|
|
945
|
+
if (!environmentSection?.children.length) continue;
|
|
946
|
+
const targetSection = ensureTopLevelSection(sceneDoc, sectionName);
|
|
947
|
+
for (const child of Array.from(environmentSection.children)) {
|
|
948
|
+
const imported = sceneDoc.importNode(child, true);
|
|
949
|
+
rewriteFileReferencesForMerge(imported, environmentFile, config.sceneFile, environmentDoc);
|
|
950
|
+
targetSection.appendChild(imported);
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
return new XMLSerializer().serializeToString(sceneDoc);
|
|
955
|
+
}
|
|
956
|
+
function findTextByConfiguredPath(textByPath, configuredPath) {
|
|
957
|
+
const normalized = normalizeVfsPath(configuredPath);
|
|
958
|
+
const direct = textByPath.get(normalized);
|
|
959
|
+
if (direct) return direct;
|
|
960
|
+
const suffix = "/" + normalized;
|
|
961
|
+
for (const [path, text] of textByPath) {
|
|
962
|
+
if (path.endsWith(suffix) || path === normalized.split("/").pop()) return text;
|
|
963
|
+
}
|
|
964
|
+
return void 0;
|
|
965
|
+
}
|
|
843
966
|
function applyXmlPatches(text, fname, config) {
|
|
844
967
|
let result = text;
|
|
845
968
|
for (const patch of config.xmlPatches ?? []) {
|
|
@@ -902,10 +1025,21 @@ async function loadSceneFromFiles(mujoco, config, onProgress) {
|
|
|
902
1025
|
if (isModelTextFile(path)) {
|
|
903
1026
|
const text = applyXmlPatches(await file.text(), path, config);
|
|
904
1027
|
textByPath.set(path, text);
|
|
905
|
-
mujoco.FS.writeFile(`/working/${path}`, text);
|
|
906
1028
|
} else {
|
|
907
1029
|
mujoco.FS.writeFile(`/working/${path}`, new Uint8Array(await file.arrayBuffer()));
|
|
1030
|
+
written.add(path);
|
|
908
1031
|
}
|
|
1032
|
+
}
|
|
1033
|
+
const environmentXmlByPath = /* @__PURE__ */ new Map();
|
|
1034
|
+
for (const environmentFile of config.environmentFiles?.map(normalizeVfsPath) ?? []) {
|
|
1035
|
+
const environmentXml = findTextByConfiguredPath(textByPath, environmentFile);
|
|
1036
|
+
if (environmentXml) environmentXmlByPath.set(environmentFile, environmentXml);
|
|
1037
|
+
}
|
|
1038
|
+
for (const [path, text] of textByPath) {
|
|
1039
|
+
const composedText = path === config.sceneFile ? composeEnvironmentXml(text, config, parser, environmentXmlByPath) : text;
|
|
1040
|
+
textByPath.set(path, composedText);
|
|
1041
|
+
ensureDir(mujoco, path);
|
|
1042
|
+
mujoco.FS.writeFile(`/working/${path}`, composedText);
|
|
909
1043
|
written.add(path);
|
|
910
1044
|
}
|
|
911
1045
|
for (const [path, text] of textByPath) {
|
|
@@ -954,6 +1088,17 @@ async function loadScene(mujoco, config, onProgress) {
|
|
|
954
1088
|
} catch {
|
|
955
1089
|
}
|
|
956
1090
|
const baseUrl = config.src.endsWith("/") ? config.src : config.src + "/";
|
|
1091
|
+
const environmentXmlByPath = /* @__PURE__ */ new Map();
|
|
1092
|
+
const environmentFiles = config.environmentFiles?.map(normalizeVfsPath) ?? [];
|
|
1093
|
+
for (const environmentFile of environmentFiles) {
|
|
1094
|
+
onProgress?.(`Downloading ${environmentFile}...`);
|
|
1095
|
+
const res = await fetch(baseUrl + environmentFile);
|
|
1096
|
+
if (!res.ok) {
|
|
1097
|
+
console.warn(`Failed to fetch environment XML ${environmentFile}: ${res.status} ${res.statusText}`);
|
|
1098
|
+
continue;
|
|
1099
|
+
}
|
|
1100
|
+
environmentXmlByPath.set(environmentFile, applyXmlPatches(await res.text(), environmentFile, config));
|
|
1101
|
+
}
|
|
957
1102
|
const downloaded = /* @__PURE__ */ new Set();
|
|
958
1103
|
const xmlQueue = [config.sceneFile];
|
|
959
1104
|
const assetFiles = [];
|
|
@@ -972,7 +1117,8 @@ async function loadScene(mujoco, config, onProgress) {
|
|
|
972
1117
|
console.warn(`Failed to fetch ${fname}: ${res.status} ${res.statusText}`);
|
|
973
1118
|
continue;
|
|
974
1119
|
}
|
|
975
|
-
const
|
|
1120
|
+
const patchedText = applyXmlPatches(await res.text(), fname, config);
|
|
1121
|
+
const text = fname === config.sceneFile ? composeEnvironmentXml(patchedText, config, parser, environmentXmlByPath) : patchedText;
|
|
976
1122
|
ensureDir(mujoco, fname);
|
|
977
1123
|
mujoco.FS.writeFile(`/working/${fname}`, text);
|
|
978
1124
|
scanDependencies(text, fname, parser, downloaded, xmlQueue);
|
|
@@ -1139,7 +1285,7 @@ function SceneRenderer(props) {
|
|
|
1139
1285
|
const model = mjModelRef.current;
|
|
1140
1286
|
if (model && bodyID < model.nbody && onSelectionRef.current) {
|
|
1141
1287
|
const name = getName(model, model.name_bodyadr[bodyID]);
|
|
1142
|
-
onSelectionRef.current(bodyID, name);
|
|
1288
|
+
onSelectionRef.current({ bodyId: bodyID, name });
|
|
1143
1289
|
}
|
|
1144
1290
|
}
|
|
1145
1291
|
}
|
|
@@ -1274,7 +1420,7 @@ function useBeforePhysicsStep(callback) {
|
|
|
1274
1420
|
const callbackRef = useRef(callback);
|
|
1275
1421
|
callbackRef.current = callback;
|
|
1276
1422
|
useEffect(() => {
|
|
1277
|
-
const wrapped = (
|
|
1423
|
+
const wrapped = (input) => callbackRef.current(input);
|
|
1278
1424
|
beforeStepCallbacks.current.add(wrapped);
|
|
1279
1425
|
return () => {
|
|
1280
1426
|
beforeStepCallbacks.current.delete(wrapped);
|
|
@@ -1286,7 +1432,7 @@ function useAfterPhysicsStep(callback) {
|
|
|
1286
1432
|
const callbackRef = useRef(callback);
|
|
1287
1433
|
callbackRef.current = callback;
|
|
1288
1434
|
useEffect(() => {
|
|
1289
|
-
const wrapped = (
|
|
1435
|
+
const wrapped = (input) => callbackRef.current(input);
|
|
1290
1436
|
afterStepCallbacks.current.add(wrapped);
|
|
1291
1437
|
return () => {
|
|
1292
1438
|
afterStepCallbacks.current.delete(wrapped);
|
|
@@ -1430,7 +1576,7 @@ function MujocoSimProvider({
|
|
|
1430
1576
|
useEffect(() => {
|
|
1431
1577
|
if (status === "ready") {
|
|
1432
1578
|
const api2 = apiRef.current;
|
|
1433
|
-
if (onReady) onReady(api2);
|
|
1579
|
+
if (onReady) onReady({ api: api2 });
|
|
1434
1580
|
if (externalApiRef) {
|
|
1435
1581
|
if (typeof externalApiRef === "function") {
|
|
1436
1582
|
externalApiRef(api2);
|
|
@@ -1450,7 +1596,7 @@ function MujocoSimProvider({
|
|
|
1450
1596
|
data.qfrc_applied[i] = 0;
|
|
1451
1597
|
}
|
|
1452
1598
|
for (const cb of beforeStepCallbacks.current) {
|
|
1453
|
-
cb(model, data);
|
|
1599
|
+
cb({ model, data });
|
|
1454
1600
|
}
|
|
1455
1601
|
const numSubsteps = substepsRef.current;
|
|
1456
1602
|
if (!interpolateRef.current) {
|
|
@@ -1501,14 +1647,14 @@ function MujocoSimProvider({
|
|
|
1501
1647
|
interpolationStateRef.current.alpha = Math.min(Math.max(physicsAccumulatorRef.current / stepDt, 0), 1);
|
|
1502
1648
|
interpolationStateRef.current.valid = true;
|
|
1503
1649
|
if (!stepped) {
|
|
1504
|
-
onStepRef.current?.(data.time);
|
|
1650
|
+
onStepRef.current?.({ time: data.time, model, data });
|
|
1505
1651
|
return;
|
|
1506
1652
|
}
|
|
1507
1653
|
}
|
|
1508
1654
|
for (const cb of afterStepCallbacks.current) {
|
|
1509
|
-
cb(model, data);
|
|
1655
|
+
cb({ model, data });
|
|
1510
1656
|
}
|
|
1511
|
-
onStepRef.current?.(data.time);
|
|
1657
|
+
onStepRef.current?.({ time: data.time, model, data });
|
|
1512
1658
|
}, -1);
|
|
1513
1659
|
function ensureInterpolationBuffers(model) {
|
|
1514
1660
|
const state = interpolationStateRef.current;
|
|
@@ -1539,7 +1685,7 @@ function MujocoSimProvider({
|
|
|
1539
1685
|
}
|
|
1540
1686
|
}
|
|
1541
1687
|
}
|
|
1542
|
-
configRef.current.onReset?.(model, data);
|
|
1688
|
+
configRef.current.onReset?.({ model, data });
|
|
1543
1689
|
mujoco.mj_forward(model, data);
|
|
1544
1690
|
for (const cb of resetCallbacks.current) {
|
|
1545
1691
|
cb();
|
|
@@ -2209,6 +2355,7 @@ var MujocoCanvas = forwardRef(
|
|
|
2209
2355
|
paused,
|
|
2210
2356
|
speed,
|
|
2211
2357
|
interpolate,
|
|
2358
|
+
loadingFallback,
|
|
2212
2359
|
children,
|
|
2213
2360
|
...canvasProps
|
|
2214
2361
|
}, ref) {
|
|
@@ -2218,7 +2365,10 @@ var MujocoCanvas = forwardRef(
|
|
|
2218
2365
|
onError(new Error(wasmError ?? "WASM load failed"));
|
|
2219
2366
|
}
|
|
2220
2367
|
}, [wasmStatus, wasmError, onError]);
|
|
2221
|
-
if (wasmStatus === "
|
|
2368
|
+
if (wasmStatus === "loading" || !mujoco) {
|
|
2369
|
+
return loadingFallback ? /* @__PURE__ */ jsx(Canvas, { ...canvasProps, children: loadingFallback }) : null;
|
|
2370
|
+
}
|
|
2371
|
+
if (wasmStatus === "error") {
|
|
2222
2372
|
return null;
|
|
2223
2373
|
}
|
|
2224
2374
|
return /* @__PURE__ */ jsx(Canvas, { ...canvasProps, children: /* @__PURE__ */ jsx(
|
|
@@ -2691,9 +2841,9 @@ var useIkController = createControllerHook(
|
|
|
2691
2841
|
}
|
|
2692
2842
|
}, [config?.siteName, config?.numJoints, config?.joints, config?.actuators, status, mjModelRef, mjDataRef, config]);
|
|
2693
2843
|
const ikSolveFn = useCallback(
|
|
2694
|
-
(
|
|
2844
|
+
({ position, quaternion, currentQ, context }) => {
|
|
2695
2845
|
if (!config) return null;
|
|
2696
|
-
if (config.ikSolveFn) return config.ikSolveFn(
|
|
2846
|
+
if (config.ikSolveFn) return config.ikSolveFn({ position, quaternion, currentQ, context });
|
|
2697
2847
|
const model = mjModelRef.current;
|
|
2698
2848
|
const data = mjDataRef.current;
|
|
2699
2849
|
const controlGroup = controlGroupRef.current;
|
|
@@ -2703,8 +2853,8 @@ var useIkController = createControllerHook(
|
|
|
2703
2853
|
data,
|
|
2704
2854
|
siteIdRef.current,
|
|
2705
2855
|
controlGroup.qposAdr,
|
|
2706
|
-
|
|
2707
|
-
|
|
2856
|
+
position,
|
|
2857
|
+
quaternion,
|
|
2708
2858
|
currentQ,
|
|
2709
2859
|
{ damping: config.damping, maxIterations: config.maxIterations }
|
|
2710
2860
|
);
|
|
@@ -2733,7 +2883,7 @@ var useIkController = createControllerHook(
|
|
|
2733
2883
|
target.quaternion.slerpQuaternions(ga.startRot, ga.endRot, ease);
|
|
2734
2884
|
if (t >= 1) ga.active = false;
|
|
2735
2885
|
});
|
|
2736
|
-
useBeforePhysicsStep((model, data) => {
|
|
2886
|
+
useBeforePhysicsStep(({ model, data }) => {
|
|
2737
2887
|
if (!config || !ikEnabledRef.current) {
|
|
2738
2888
|
ikCalculatingRef.current = false;
|
|
2739
2889
|
return;
|
|
@@ -2744,12 +2894,21 @@ var useIkController = createControllerHook(
|
|
|
2744
2894
|
const controlGroup = controlGroupRef.current;
|
|
2745
2895
|
if (!controlGroup) return;
|
|
2746
2896
|
const currentQ = Array.from(controlGroup.readQpos(data));
|
|
2747
|
-
const solution = config.ikSolveFn ? config.ikSolveFn(
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2897
|
+
const solution = config.ikSolveFn ? config.ikSolveFn({
|
|
2898
|
+
position: target.position,
|
|
2899
|
+
quaternion: target.quaternion,
|
|
2900
|
+
currentQ,
|
|
2901
|
+
context: {
|
|
2902
|
+
model,
|
|
2903
|
+
data,
|
|
2904
|
+
siteId: siteIdRef.current,
|
|
2905
|
+
controlGroup
|
|
2906
|
+
}
|
|
2907
|
+
}) : ikSolveFnRef.current({
|
|
2908
|
+
position: target.position,
|
|
2909
|
+
quaternion: target.quaternion,
|
|
2910
|
+
currentQ
|
|
2911
|
+
});
|
|
2753
2912
|
if (solution) {
|
|
2754
2913
|
controlGroup.writeCtrl(data, solution);
|
|
2755
2914
|
}
|
|
@@ -2788,8 +2947,8 @@ var useIkController = createControllerHook(
|
|
|
2788
2947
|
if (data && target) syncGizmoToSite(data, siteIdRef.current, target);
|
|
2789
2948
|
}, [mjDataRef]);
|
|
2790
2949
|
const solveIK = useCallback(
|
|
2791
|
-
(
|
|
2792
|
-
return ikSolveFnRef.current(
|
|
2950
|
+
(input) => {
|
|
2951
|
+
return ikSolveFnRef.current(input);
|
|
2793
2952
|
},
|
|
2794
2953
|
[]
|
|
2795
2954
|
);
|
|
@@ -2860,6 +3019,7 @@ function Body({
|
|
|
2860
3019
|
solref,
|
|
2861
3020
|
solimp,
|
|
2862
3021
|
condim,
|
|
3022
|
+
group,
|
|
2863
3023
|
children
|
|
2864
3024
|
}) {
|
|
2865
3025
|
const { bodyRegistryRef, hiddenBodiesRef, requestBodyReload, mjDataRef, mjModelRef, status } = useMujocoContext();
|
|
@@ -2879,7 +3039,8 @@ function Body({
|
|
|
2879
3039
|
friction,
|
|
2880
3040
|
solref,
|
|
2881
3041
|
solimp,
|
|
2882
|
-
condim
|
|
3042
|
+
condim,
|
|
3043
|
+
group
|
|
2883
3044
|
};
|
|
2884
3045
|
bodyRegistryRef.current.set(name, { definition, hasCustomChildren: hasChildren });
|
|
2885
3046
|
if (hasChildren) {
|
|
@@ -2892,7 +3053,7 @@ function Body({
|
|
|
2892
3053
|
requestBodyReload();
|
|
2893
3054
|
}
|
|
2894
3055
|
};
|
|
2895
|
-
}, [name, type, size, position, rgba, mass, freejoint, friction, solref, solimp, condim, hasChildren, bodyRegistryRef, hiddenBodiesRef, requestBodyReload]);
|
|
3056
|
+
}, [name, type, size, position, rgba, mass, freejoint, friction, solref, solimp, condim, group, hasChildren, bodyRegistryRef, hiddenBodiesRef, requestBodyReload]);
|
|
2896
3057
|
useEffect(() => {
|
|
2897
3058
|
if (status !== "ready") return;
|
|
2898
3059
|
const model = mjModelRef.current;
|
|
@@ -2904,12 +3065,12 @@ function Body({
|
|
|
2904
3065
|
if (!hasChildren) return;
|
|
2905
3066
|
const data = mjDataRef.current;
|
|
2906
3067
|
const id = bodyIdRef.current;
|
|
2907
|
-
const
|
|
2908
|
-
if (!data || id < 0 || !
|
|
3068
|
+
const group2 = groupRef.current;
|
|
3069
|
+
if (!data || id < 0 || !group2) return;
|
|
2909
3070
|
const i3 = id * 3;
|
|
2910
3071
|
const i4 = id * 4;
|
|
2911
|
-
|
|
2912
|
-
|
|
3072
|
+
group2.position.set(data.xpos[i3], data.xpos[i3 + 1], data.xpos[i3 + 2]);
|
|
3073
|
+
group2.quaternion.set(
|
|
2913
3074
|
data.xquat[i4 + 1],
|
|
2914
3075
|
data.xquat[i4 + 2],
|
|
2915
3076
|
data.xquat[i4 + 3],
|
|
@@ -3001,7 +3162,7 @@ function IkGizmo({ controller, siteName, scale = 0.18, onDrag }) {
|
|
|
3001
3162
|
onDrag: (_l, _dl, world) => {
|
|
3002
3163
|
world.decompose(_pos, _quat, _scale);
|
|
3003
3164
|
if (onDrag) {
|
|
3004
|
-
onDrag(_pos.clone(), _quat.clone());
|
|
3165
|
+
onDrag({ position: _pos.clone(), quaternion: _quat.clone() });
|
|
3005
3166
|
} else {
|
|
3006
3167
|
const target = ikTargetRef.current;
|
|
3007
3168
|
if (target) {
|
|
@@ -3175,7 +3336,7 @@ function DragInteraction({
|
|
|
3175
3336
|
window.removeEventListener("pointercancel", onPointerUp);
|
|
3176
3337
|
};
|
|
3177
3338
|
}, [gl, camera, scene, controls, mjDataRef]);
|
|
3178
|
-
useBeforePhysicsStep((model, data) => {
|
|
3339
|
+
useBeforePhysicsStep(({ model, data }) => {
|
|
3179
3340
|
if (!draggingRef.current || bodyIdRef.current <= 0) return;
|
|
3180
3341
|
const bid = bodyIdRef.current;
|
|
3181
3342
|
const mujoco = mujocoRef.current;
|
|
@@ -3966,7 +4127,7 @@ function useContacts(bodyName, callback) {
|
|
|
3966
4127
|
bodyIdRef.current = findBodyByName(model, bodyName);
|
|
3967
4128
|
bodyResolvedRef.current = true;
|
|
3968
4129
|
}, [bodyName, status, mjModelRef]);
|
|
3969
|
-
useAfterPhysicsStep((model, data) => {
|
|
4130
|
+
useAfterPhysicsStep(({ model, data }) => {
|
|
3970
4131
|
if (bodyName && !bodyResolvedRef.current) {
|
|
3971
4132
|
bodyIdRef.current = findBodyByName(model, bodyName);
|
|
3972
4133
|
bodyResolvedRef.current = true;
|
|
@@ -4072,7 +4233,7 @@ function useTrajectoryPlayer(trajectory, options = {}) {
|
|
|
4072
4233
|
const setState = useCallback((next) => {
|
|
4073
4234
|
if (stateRef.current === next) return;
|
|
4074
4235
|
stateRef.current = next;
|
|
4075
|
-
optionsRef.current.onStateChange?.(next);
|
|
4236
|
+
optionsRef.current.onStateChange?.({ state: next });
|
|
4076
4237
|
}, []);
|
|
4077
4238
|
const play = useCallback(() => {
|
|
4078
4239
|
const traj = trajectoryRef.current;
|
|
@@ -4154,7 +4315,7 @@ function useTrajectoryPlayer(trajectory, options = {}) {
|
|
|
4154
4315
|
}
|
|
4155
4316
|
}
|
|
4156
4317
|
});
|
|
4157
|
-
useBeforePhysicsStep((model, data) => {
|
|
4318
|
+
useBeforePhysicsStep(({ model, data }) => {
|
|
4158
4319
|
if (stateRef.current !== "playing") return;
|
|
4159
4320
|
if ((optionsRef.current.mode ?? "kinematic") !== "physics") return;
|
|
4160
4321
|
const traj = trajectoryRef.current;
|
|
@@ -4239,7 +4400,10 @@ function TrajectoryPlayer({
|
|
|
4239
4400
|
const currentFrame = player.frame;
|
|
4240
4401
|
if (currentFrame !== lastReportedFrameRef.current && player.playing) {
|
|
4241
4402
|
lastReportedFrameRef.current = currentFrame;
|
|
4242
|
-
onFrameRef.current(
|
|
4403
|
+
onFrameRef.current({
|
|
4404
|
+
frameIndex: currentFrame,
|
|
4405
|
+
frame: trajectory[currentFrame]
|
|
4406
|
+
});
|
|
4243
4407
|
}
|
|
4244
4408
|
});
|
|
4245
4409
|
return null;
|
|
@@ -4313,7 +4477,7 @@ function useSitePosition(siteName) {
|
|
|
4313
4477
|
|
|
4314
4478
|
// src/hooks/useGravityCompensation.ts
|
|
4315
4479
|
function useGravityCompensation(enabled = true) {
|
|
4316
|
-
useBeforePhysicsStep((model, data) => {
|
|
4480
|
+
useBeforePhysicsStep(({ model, data }) => {
|
|
4317
4481
|
if (!enabled) return;
|
|
4318
4482
|
for (let i = 0; i < model.nv; i++) {
|
|
4319
4483
|
data.qfrc_applied[i] += data.qfrc_bias[i];
|
|
@@ -4340,7 +4504,7 @@ function useSensor(name) {
|
|
|
4340
4504
|
}
|
|
4341
4505
|
sensorIdRef.current = -1;
|
|
4342
4506
|
}, [name, status, mjModelRef]);
|
|
4343
|
-
useAfterPhysicsStep((
|
|
4507
|
+
useAfterPhysicsStep(({ data }) => {
|
|
4344
4508
|
if (sensorIdRef.current < 0) return;
|
|
4345
4509
|
const adr = sensorAdrRef.current;
|
|
4346
4510
|
const dim = sensorDimRef.current;
|
|
@@ -4437,7 +4601,7 @@ function useJointState(name) {
|
|
|
4437
4601
|
}
|
|
4438
4602
|
jointIdRef.current = -1;
|
|
4439
4603
|
}, [name, status, mjModelRef]);
|
|
4440
|
-
useAfterPhysicsStep((
|
|
4604
|
+
useAfterPhysicsStep(({ data }) => {
|
|
4441
4605
|
if (jointIdRef.current < 0) return;
|
|
4442
4606
|
const qa = qposAdrRef.current;
|
|
4443
4607
|
const da = dofAdrRef.current;
|
|
@@ -4467,7 +4631,7 @@ function useBodyState(name) {
|
|
|
4467
4631
|
if (!model || status !== "ready") return;
|
|
4468
4632
|
bodyIdRef.current = findBodyByName(model, name);
|
|
4469
4633
|
}, [name, status, mjModelRef]);
|
|
4470
|
-
useAfterPhysicsStep((
|
|
4634
|
+
useAfterPhysicsStep(({ data }) => {
|
|
4471
4635
|
const bid = bodyIdRef.current;
|
|
4472
4636
|
if (bid < 0) return;
|
|
4473
4637
|
const i3 = bid * 3;
|
|
@@ -4563,7 +4727,7 @@ function useKeyboardTeleop(config) {
|
|
|
4563
4727
|
window.removeEventListener("keyup", onKeyUp);
|
|
4564
4728
|
};
|
|
4565
4729
|
}, []);
|
|
4566
|
-
useBeforePhysicsStep((
|
|
4730
|
+
useBeforePhysicsStep(({ data }) => {
|
|
4567
4731
|
if (!enabledRef.current) return;
|
|
4568
4732
|
const bindings = bindingsRef.current;
|
|
4569
4733
|
const cache = actuatorCacheRef.current;
|
|
@@ -4584,22 +4748,25 @@ function useKeyboardTeleop(config) {
|
|
|
4584
4748
|
});
|
|
4585
4749
|
}
|
|
4586
4750
|
function usePolicy(config) {
|
|
4587
|
-
const { mjModelRef } = useMujocoContext();
|
|
4588
4751
|
const lastActionTimeRef = useRef(0);
|
|
4752
|
+
const lastObservationRef = useRef(null);
|
|
4589
4753
|
const lastActionRef = useRef(null);
|
|
4590
|
-
const isRunningRef = useRef(true);
|
|
4754
|
+
const isRunningRef = useRef(config.enabled ?? true);
|
|
4591
4755
|
const configRef = useRef(config);
|
|
4592
4756
|
configRef.current = config;
|
|
4593
|
-
|
|
4757
|
+
isRunningRef.current = config.enabled ?? isRunningRef.current;
|
|
4758
|
+
useBeforePhysicsStep(({ model, data }) => {
|
|
4594
4759
|
if (!isRunningRef.current) return;
|
|
4595
4760
|
const cfg = configRef.current;
|
|
4596
4761
|
model.opt?.timestep ?? 2e-3;
|
|
4597
4762
|
const interval = 1 / cfg.frequency;
|
|
4598
4763
|
if (data.time - lastActionTimeRef.current >= interval) {
|
|
4599
|
-
const
|
|
4600
|
-
cfg.
|
|
4764
|
+
const observation = cfg.onObservation({ model, data });
|
|
4765
|
+
const action = cfg.infer ? cfg.infer({ observation, model, data }) : observation;
|
|
4766
|
+
cfg.onAction({ action, observation, model, data });
|
|
4601
4767
|
lastActionTimeRef.current = data.time;
|
|
4602
|
-
|
|
4768
|
+
lastObservationRef.current = observation;
|
|
4769
|
+
lastActionRef.current = action;
|
|
4603
4770
|
}
|
|
4604
4771
|
});
|
|
4605
4772
|
return {
|
|
@@ -4613,6 +4780,9 @@ function usePolicy(config) {
|
|
|
4613
4780
|
isRunningRef.current = false;
|
|
4614
4781
|
},
|
|
4615
4782
|
get lastObservation() {
|
|
4783
|
+
return lastObservationRef.current;
|
|
4784
|
+
},
|
|
4785
|
+
get lastAction() {
|
|
4616
4786
|
return lastActionRef.current;
|
|
4617
4787
|
}
|
|
4618
4788
|
};
|
|
@@ -4642,7 +4812,7 @@ function useTrajectoryRecorder(options = {}) {
|
|
|
4642
4812
|
const recordingRef = useRef(false);
|
|
4643
4813
|
const framesRef = useRef([]);
|
|
4644
4814
|
const fields = options.fields ?? ["qpos"];
|
|
4645
|
-
useAfterPhysicsStep((
|
|
4815
|
+
useAfterPhysicsStep(({ data }) => {
|
|
4646
4816
|
if (!recordingRef.current) return;
|
|
4647
4817
|
const frame = {
|
|
4648
4818
|
time: data.time,
|
|
@@ -4731,7 +4901,7 @@ function useGamepad(config) {
|
|
|
4731
4901
|
buttonCacheRef.current.set(Number(idx), findActuatorByName(model, name));
|
|
4732
4902
|
}
|
|
4733
4903
|
}, [config.axes, config.buttons, status, mjModelRef]);
|
|
4734
|
-
useBeforePhysicsStep((
|
|
4904
|
+
useBeforePhysicsStep(({ data }) => {
|
|
4735
4905
|
const cfg = configRef.current;
|
|
4736
4906
|
if (cfg.enabled === false) return;
|
|
4737
4907
|
const gamepads = navigator.getGamepads?.();
|
|
@@ -4809,12 +4979,120 @@ function useVideoRecorder(options = {}) {
|
|
|
4809
4979
|
}
|
|
4810
4980
|
};
|
|
4811
4981
|
}
|
|
4982
|
+
function isTargetRef(target) {
|
|
4983
|
+
return Boolean(target && typeof target === "object" && "current" in target);
|
|
4984
|
+
}
|
|
4985
|
+
function resolveCanvasTarget(target) {
|
|
4986
|
+
const resolvedTarget = isTargetRef(target) ? target.current : target;
|
|
4987
|
+
if (!resolvedTarget) {
|
|
4988
|
+
throw new Error("No frame capture target is available.");
|
|
4989
|
+
}
|
|
4990
|
+
if (resolvedTarget instanceof HTMLCanvasElement) {
|
|
4991
|
+
return resolvedTarget;
|
|
4992
|
+
}
|
|
4993
|
+
const canvas = resolvedTarget.querySelector("canvas");
|
|
4994
|
+
if (!canvas) {
|
|
4995
|
+
throw new Error("Frame capture target does not contain a canvas.");
|
|
4996
|
+
}
|
|
4997
|
+
return canvas;
|
|
4998
|
+
}
|
|
4999
|
+
function waitForNextAnimationFrame() {
|
|
5000
|
+
return new Promise((resolve) => {
|
|
5001
|
+
requestAnimationFrame(() => resolve());
|
|
5002
|
+
});
|
|
5003
|
+
}
|
|
5004
|
+
async function captureFrame(options) {
|
|
5005
|
+
const type = options.type ?? "image/png";
|
|
5006
|
+
const canvas = resolveCanvasTarget(options.target);
|
|
5007
|
+
if (options.waitForAnimationFrame ?? true) {
|
|
5008
|
+
await waitForNextAnimationFrame();
|
|
5009
|
+
}
|
|
5010
|
+
return {
|
|
5011
|
+
canvas,
|
|
5012
|
+
dataUrl: canvas.toDataURL(type, options.quality),
|
|
5013
|
+
type
|
|
5014
|
+
};
|
|
5015
|
+
}
|
|
5016
|
+
async function captureFrameBlob(options) {
|
|
5017
|
+
const type = options.type ?? "image/png";
|
|
5018
|
+
const canvas = resolveCanvasTarget(options.target);
|
|
5019
|
+
if (options.waitForAnimationFrame ?? true) {
|
|
5020
|
+
await waitForNextAnimationFrame();
|
|
5021
|
+
}
|
|
5022
|
+
const blob = await new Promise((resolve, reject) => {
|
|
5023
|
+
canvas.toBlob(
|
|
5024
|
+
(nextBlob) => {
|
|
5025
|
+
if (nextBlob) {
|
|
5026
|
+
resolve(nextBlob);
|
|
5027
|
+
} else {
|
|
5028
|
+
reject(new Error("Canvas frame capture did not produce a Blob."));
|
|
5029
|
+
}
|
|
5030
|
+
},
|
|
5031
|
+
type,
|
|
5032
|
+
options.quality
|
|
5033
|
+
);
|
|
5034
|
+
});
|
|
5035
|
+
return { canvas, blob, type };
|
|
5036
|
+
}
|
|
5037
|
+
function useFrameCapture(defaultOptions = {}) {
|
|
5038
|
+
const [status, setStatus] = useState("idle");
|
|
5039
|
+
const [error, setError] = useState(null);
|
|
5040
|
+
const reset = useCallback(() => {
|
|
5041
|
+
setStatus("idle");
|
|
5042
|
+
setError(null);
|
|
5043
|
+
}, []);
|
|
5044
|
+
const capture = useCallback(
|
|
5045
|
+
async (options = {}) => {
|
|
5046
|
+
setStatus("capturing");
|
|
5047
|
+
setError(null);
|
|
5048
|
+
try {
|
|
5049
|
+
const result = await captureFrame({ ...defaultOptions, ...options });
|
|
5050
|
+
setStatus("captured");
|
|
5051
|
+
return result;
|
|
5052
|
+
} catch (nextError) {
|
|
5053
|
+
const error2 = nextError instanceof Error ? nextError : new Error("Unable to capture the current canvas frame.");
|
|
5054
|
+
setError(error2);
|
|
5055
|
+
setStatus("error");
|
|
5056
|
+
throw error2;
|
|
5057
|
+
}
|
|
5058
|
+
},
|
|
5059
|
+
[defaultOptions]
|
|
5060
|
+
);
|
|
5061
|
+
const captureBlob = useCallback(
|
|
5062
|
+
async (options = {}) => {
|
|
5063
|
+
setStatus("capturing");
|
|
5064
|
+
setError(null);
|
|
5065
|
+
try {
|
|
5066
|
+
const result = await captureFrameBlob({
|
|
5067
|
+
...defaultOptions,
|
|
5068
|
+
...options
|
|
5069
|
+
});
|
|
5070
|
+
setStatus("captured");
|
|
5071
|
+
return result;
|
|
5072
|
+
} catch (nextError) {
|
|
5073
|
+
const error2 = nextError instanceof Error ? nextError : new Error("Unable to capture the current canvas frame.");
|
|
5074
|
+
setError(error2);
|
|
5075
|
+
setStatus("error");
|
|
5076
|
+
throw error2;
|
|
5077
|
+
}
|
|
5078
|
+
},
|
|
5079
|
+
[defaultOptions]
|
|
5080
|
+
);
|
|
5081
|
+
return {
|
|
5082
|
+
status,
|
|
5083
|
+
error,
|
|
5084
|
+
isCapturing: status === "capturing",
|
|
5085
|
+
capture,
|
|
5086
|
+
captureBlob,
|
|
5087
|
+
reset
|
|
5088
|
+
};
|
|
5089
|
+
}
|
|
4812
5090
|
function useCtrlNoise(config = {}) {
|
|
4813
5091
|
const { mjModelRef } = useMujocoContext();
|
|
4814
5092
|
const configRef = useRef(config);
|
|
4815
5093
|
configRef.current = config;
|
|
4816
5094
|
const noiseRef = useRef(null);
|
|
4817
|
-
useBeforePhysicsStep((
|
|
5095
|
+
useBeforePhysicsStep(({ data }) => {
|
|
4818
5096
|
const cfg = configRef.current;
|
|
4819
5097
|
if (cfg.enabled === false) return;
|
|
4820
5098
|
const rate = cfg.rate ?? 0.01;
|
|
@@ -5094,6 +5372,12 @@ function useCameraAnimation() {
|
|
|
5094
5372
|
*
|
|
5095
5373
|
* useVideoRecorder — canvas video recording hook (spec 13.3)
|
|
5096
5374
|
*/
|
|
5375
|
+
/**
|
|
5376
|
+
* @license
|
|
5377
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5378
|
+
*
|
|
5379
|
+
* useFrameCapture — still-frame capture for canvas-backed MuJoCo/R3F scenes.
|
|
5380
|
+
*/
|
|
5097
5381
|
/**
|
|
5098
5382
|
* @license
|
|
5099
5383
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -5124,6 +5408,6 @@ function useCameraAnimation() {
|
|
|
5124
5408
|
* useCameraAnimation — composable camera animation hook.
|
|
5125
5409
|
*/
|
|
5126
5410
|
|
|
5127
|
-
export { Body, ContactListener, ContactMarkers, Debug, DragInteraction, FlexRenderer, IkGizmo, InstancedGeomRenderer, MujocoCanvas, MujocoPhysics, MujocoProvider, MujocoSimProvider, RobotActuators, RobotBodies, RobotGeoms, RobotJoints, RobotKeyframes, RobotResources, RobotSensors, RobotSites, SceneLights, TendonRenderer, TrajectoryPlayer, buildObservation, createContiguousControlGroup, createController, createControllerHook, findActuatorByName, findBodyByName, findGeomByName, findJointByName, findKeyframeByName, findSensorByName, findSiteByName, findTendonByName, getActuatedJoints, getContact, getControlMap, getName, loadScene, registerRobotResources, resolveControlGroup, useActuators, useAfterPhysicsStep, useBeforePhysicsStep, useBodyMeshes, useBodyState, useCameraAnimation, useContactEvents, useContacts, useCtrl, useCtrlNoise, useGamepad, useGravityCompensation, useIkController, useJointState, useKeyboardTeleop, useMujoco, useMujocoWasm, useObservation, usePolicy, useSceneLights, useSelectionHighlight, useSensor, useSensors, useSitePosition, useTrajectoryPlayer, useTrajectoryRecorder, useVideoRecorder };
|
|
5411
|
+
export { Body, ContactListener, ContactMarkers, Debug, DragInteraction, FlexRenderer, IkGizmo, InstancedGeomRenderer, MujocoCanvas, MujocoPhysics, MujocoProvider, MujocoSimProvider, RobotActuators, RobotBodies, RobotGeoms, RobotJoints, RobotKeyframes, RobotResources, RobotSensors, RobotSites, SceneLights, TendonRenderer, TrajectoryPlayer, buildObservation, captureFrame, captureFrameBlob, createContiguousControlGroup, createController, createControllerHook, findActuatorByName, findBodyByName, findGeomByName, findJointByName, findKeyframeByName, findSensorByName, findSiteByName, findTendonByName, getActuatedJoints, getContact, getControlMap, getName, loadScene, registerRobotResources, resolveControlGroup, useActuators, useAfterPhysicsStep, useBeforePhysicsStep, useBodyMeshes, useBodyState, useCameraAnimation, useContactEvents, useContacts, useCtrl, useCtrlNoise, useFrameCapture, useGamepad, useGravityCompensation, useIkController, useJointState, useKeyboardTeleop, useMujoco, useMujocoWasm, useObservation, usePolicy, useSceneLights, useSelectionHighlight, useSensor, useSensors, useSitePosition, useTrajectoryPlayer, useTrajectoryRecorder, useVideoRecorder };
|
|
5128
5412
|
//# sourceMappingURL=index.js.map
|
|
5129
5413
|
//# sourceMappingURL=index.js.map
|