mujoco-react 9.3.0 → 9.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +139 -15
- package/dist/{chunk-T3GVZJ4F.js → chunk-VDSEPZYQ.js} +107 -32
- package/dist/chunk-VDSEPZYQ.js.map +1 -0
- package/dist/index.d.ts +119 -6
- package/dist/index.js +489 -10
- package/dist/index.js.map +1 -1
- package/dist/spark.d.ts +1 -1
- package/dist/spark.js +1 -1
- package/dist/{types-oxbxOkAx.d.ts → types-BuJ4boaq.d.ts} +41 -3
- package/dist/vite.js +8 -4
- package/dist/vite.js.map +1 -1
- package/package.json +1 -1
- package/src/components/SplatCollisionProxyPreview.tsx +350 -0
- package/src/components/VisualScenario.tsx +140 -41
- package/src/core/MujocoSimProvider.tsx +1 -1
- package/src/hooks/useMountedCameraSequenceRecorder.ts +54 -6
- package/src/index.ts +31 -0
- package/src/rendering/cameraFrameSource.ts +372 -0
- package/src/types.ts +44 -2
- package/src/vite.ts +9 -4
- package/dist/chunk-T3GVZJ4F.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { withContacts, getContact } from './chunk-
|
|
2
|
-
export { RobotActuators, RobotBodies, RobotCameras, RobotGeoms, RobotJoints, RobotKeyframes, RobotResources, RobotSensors, RobotSites, ScenarioLighting, SplatEnvironment, SplatEnvironmentReadinessStatus, VisualScenarioEffects, createPairedSplatEnvironment, createSparkSplatViewerUrl, createSplatEnvironmentUserData, getContact, getScenarioBackground, getScenarioCameraPosition, getSplatEnvironmentReadiness, registerRobotResources, useSplatEnvironment, useSplatSceneConfig, useVisualScenarioEffects, withSplatEnvironment } from './chunk-
|
|
1
|
+
import { withContacts, getContact } from './chunk-VDSEPZYQ.js';
|
|
2
|
+
export { RobotActuators, RobotBodies, RobotCameras, RobotGeoms, RobotJoints, RobotKeyframes, RobotResources, RobotSensors, RobotSites, ScenarioLighting, SplatEnvironment, SplatEnvironmentReadinessStatus, VisualScenarioEffects, createPairedSplatEnvironment, createSparkSplatViewerUrl, createSplatEnvironmentUserData, createSplatSceneConfig, createVisualScenarioExecutionContext, getContact, getScenarioBackground, getScenarioCameraPosition, getSplatEnvironmentReadiness, registerRobotResources, useSplatEnvironment, useSplatSceneConfig, useVisualScenarioEffects, useVisualScenarioExecutionContext, withSplatEnvironment } from './chunk-VDSEPZYQ.js';
|
|
3
3
|
import loadMujoco from '@mujoco/mujoco';
|
|
4
4
|
import defaultMujocoWasmUrl from '@mujoco/mujoco/mujoco.wasm?url';
|
|
5
5
|
import { createContext, forwardRef, useEffect, useContext, useState, useRef, useCallback, useMemo, useLayoutEffect } from 'react';
|
|
@@ -1578,11 +1578,24 @@ async function captureCameraFrameBlob(renderer, scene, fallbackCamera, options =
|
|
|
1578
1578
|
}
|
|
1579
1579
|
|
|
1580
1580
|
// src/rendering/cameraFrameSource.ts
|
|
1581
|
+
var MountedCameraFrameSourceSuggestionMatch = {
|
|
1582
|
+
Direct: "direct",
|
|
1583
|
+
Alias: "alias",
|
|
1584
|
+
Normalized: "normalized",
|
|
1585
|
+
Prefix: "prefix",
|
|
1586
|
+
Suffix: "suffix",
|
|
1587
|
+
Contains: "contains"
|
|
1588
|
+
};
|
|
1581
1589
|
var MountedCameraFrameSequenceReadinessStatus = {
|
|
1582
1590
|
Ready: "ready",
|
|
1583
1591
|
Partial: "partial",
|
|
1584
1592
|
Missing: "missing"
|
|
1585
1593
|
};
|
|
1594
|
+
var MountedCameraFrameSequenceManifestStatus = {
|
|
1595
|
+
Complete: "complete",
|
|
1596
|
+
Partial: "partial",
|
|
1597
|
+
Missing: "missing"
|
|
1598
|
+
};
|
|
1586
1599
|
function getResourceName(resource) {
|
|
1587
1600
|
if (!resource) return null;
|
|
1588
1601
|
return typeof resource === "string" ? resource : resource.name ?? null;
|
|
@@ -1592,6 +1605,9 @@ function createNameSet(resources) {
|
|
|
1592
1605
|
(resources ?? []).map((resource) => getResourceName(resource)).filter((name) => Boolean(name))
|
|
1593
1606
|
);
|
|
1594
1607
|
}
|
|
1608
|
+
function createResourceNames(resources) {
|
|
1609
|
+
return (resources ?? []).map((resource) => getResourceName(resource)).filter((name) => Boolean(name));
|
|
1610
|
+
}
|
|
1595
1611
|
function normalizeAliasCandidates(value) {
|
|
1596
1612
|
if (!value) return [];
|
|
1597
1613
|
return Array.isArray(value) ? value : [value];
|
|
@@ -1599,6 +1615,24 @@ function normalizeAliasCandidates(value) {
|
|
|
1599
1615
|
function countMountedSelectors(selector) {
|
|
1600
1616
|
return Number(Boolean(selector.cameraName)) + Number(Boolean(selector.siteName)) + Number(Boolean(selector.bodyName));
|
|
1601
1617
|
}
|
|
1618
|
+
function normalizeCameraSourceName(value) {
|
|
1619
|
+
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
1620
|
+
}
|
|
1621
|
+
function createCameraSourceKeyVariants(key) {
|
|
1622
|
+
const candidates = [
|
|
1623
|
+
key,
|
|
1624
|
+
key.startsWith("observation.images.") ? key.slice("observation.images.".length) : "",
|
|
1625
|
+
key.includes(".") ? key.split(".").at(-1) ?? "" : "",
|
|
1626
|
+
key.includes("/") ? key.split("/").at(-1) ?? "" : ""
|
|
1627
|
+
];
|
|
1628
|
+
return candidates.map((candidate) => candidate.trim()).filter((candidate, index, items) => candidate && items.indexOf(candidate) === index);
|
|
1629
|
+
}
|
|
1630
|
+
function getSelectorKey(selector) {
|
|
1631
|
+
if (selector.cameraName) return `camera:${selector.cameraName}`;
|
|
1632
|
+
if (selector.siteName) return `site:${selector.siteName}`;
|
|
1633
|
+
if (selector.bodyName) return `body:${selector.bodyName}`;
|
|
1634
|
+
return null;
|
|
1635
|
+
}
|
|
1602
1636
|
function getMountedCameraFrameCaptureSource(selector) {
|
|
1603
1637
|
if (countMountedSelectors(selector) !== 1) return null;
|
|
1604
1638
|
if (selector.cameraName) {
|
|
@@ -1623,10 +1657,119 @@ function getCameraFrameCaptureSourceTarget(source) {
|
|
|
1623
1657
|
if (source.kind === "explicit-pose") return "explicit pose";
|
|
1624
1658
|
return "fallback camera";
|
|
1625
1659
|
}
|
|
1660
|
+
function createMountedCameraFrameSourceSuggestion(key, selector, resourceName, match) {
|
|
1661
|
+
const source = getMountedCameraFrameCaptureSource(selector);
|
|
1662
|
+
if (!source) return null;
|
|
1663
|
+
return {
|
|
1664
|
+
key,
|
|
1665
|
+
selector,
|
|
1666
|
+
source,
|
|
1667
|
+
resourceName,
|
|
1668
|
+
resourceKind: source.kind,
|
|
1669
|
+
match
|
|
1670
|
+
};
|
|
1671
|
+
}
|
|
1672
|
+
function addMountedCameraFrameSourceSuggestion(suggestions, seen, suggestion) {
|
|
1673
|
+
if (!suggestion) return;
|
|
1674
|
+
const selectorKey = getSelectorKey(suggestion.selector);
|
|
1675
|
+
if (!selectorKey || seen.has(selectorKey)) return;
|
|
1676
|
+
seen.add(selectorKey);
|
|
1677
|
+
suggestions.push(suggestion);
|
|
1678
|
+
}
|
|
1679
|
+
function getCameraFrameResourceMatch(key, resourceName) {
|
|
1680
|
+
if (resourceName === key) return MountedCameraFrameSourceSuggestionMatch.Direct;
|
|
1681
|
+
const normalizedResource = normalizeCameraSourceName(resourceName);
|
|
1682
|
+
if (!normalizedResource) return null;
|
|
1683
|
+
for (const variant of createCameraSourceKeyVariants(key)) {
|
|
1684
|
+
if (resourceName === variant) return MountedCameraFrameSourceSuggestionMatch.Direct;
|
|
1685
|
+
const normalizedKey = normalizeCameraSourceName(variant);
|
|
1686
|
+
if (!normalizedKey) continue;
|
|
1687
|
+
if (normalizedResource === normalizedKey) {
|
|
1688
|
+
return MountedCameraFrameSourceSuggestionMatch.Normalized;
|
|
1689
|
+
}
|
|
1690
|
+
if (normalizedResource.startsWith(`${normalizedKey}_`)) {
|
|
1691
|
+
return MountedCameraFrameSourceSuggestionMatch.Prefix;
|
|
1692
|
+
}
|
|
1693
|
+
if (normalizedResource.endsWith(`_${normalizedKey}`)) {
|
|
1694
|
+
return MountedCameraFrameSourceSuggestionMatch.Suffix;
|
|
1695
|
+
}
|
|
1696
|
+
if (normalizedResource.includes(`_${normalizedKey}_`)) {
|
|
1697
|
+
return MountedCameraFrameSourceSuggestionMatch.Contains;
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
return null;
|
|
1701
|
+
}
|
|
1626
1702
|
function isSelectorMounted(selector, cameraNames, siteNames, bodyNames) {
|
|
1627
1703
|
if (countMountedSelectors(selector) !== 1) return false;
|
|
1628
1704
|
return (selector.cameraName ? cameraNames.has(selector.cameraName) : false) || (selector.siteName ? siteNames.has(selector.siteName) : false) || (selector.bodyName ? bodyNames.has(selector.bodyName) : false);
|
|
1629
1705
|
}
|
|
1706
|
+
function createMountedCameraFrameSourceSuggestions(key, options) {
|
|
1707
|
+
const cameraNames = createNameSet(options.cameras);
|
|
1708
|
+
const siteNames = createNameSet(options.sites);
|
|
1709
|
+
const bodyNames = createNameSet(options.bodies);
|
|
1710
|
+
const suggestions = [];
|
|
1711
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1712
|
+
for (const selector of normalizeAliasCandidates(options.aliases?.[key])) {
|
|
1713
|
+
if (!isSelectorMounted(selector, cameraNames, siteNames, bodyNames)) {
|
|
1714
|
+
continue;
|
|
1715
|
+
}
|
|
1716
|
+
const source = getMountedCameraFrameCaptureSource(selector);
|
|
1717
|
+
if (!source) continue;
|
|
1718
|
+
addMountedCameraFrameSourceSuggestion(
|
|
1719
|
+
suggestions,
|
|
1720
|
+
seen,
|
|
1721
|
+
createMountedCameraFrameSourceSuggestion(
|
|
1722
|
+
key,
|
|
1723
|
+
selector,
|
|
1724
|
+
getCameraFrameCaptureSourceTarget(source),
|
|
1725
|
+
MountedCameraFrameSourceSuggestionMatch.Alias
|
|
1726
|
+
)
|
|
1727
|
+
);
|
|
1728
|
+
}
|
|
1729
|
+
for (const cameraName of createResourceNames(options.cameras)) {
|
|
1730
|
+
const match = getCameraFrameResourceMatch(key, cameraName);
|
|
1731
|
+
if (!match) continue;
|
|
1732
|
+
addMountedCameraFrameSourceSuggestion(
|
|
1733
|
+
suggestions,
|
|
1734
|
+
seen,
|
|
1735
|
+
createMountedCameraFrameSourceSuggestion(
|
|
1736
|
+
key,
|
|
1737
|
+
{ cameraName },
|
|
1738
|
+
cameraName,
|
|
1739
|
+
match
|
|
1740
|
+
)
|
|
1741
|
+
);
|
|
1742
|
+
}
|
|
1743
|
+
for (const siteName of createResourceNames(options.sites)) {
|
|
1744
|
+
const match = getCameraFrameResourceMatch(key, siteName);
|
|
1745
|
+
if (!match) continue;
|
|
1746
|
+
addMountedCameraFrameSourceSuggestion(
|
|
1747
|
+
suggestions,
|
|
1748
|
+
seen,
|
|
1749
|
+
createMountedCameraFrameSourceSuggestion(
|
|
1750
|
+
key,
|
|
1751
|
+
{ siteName },
|
|
1752
|
+
siteName,
|
|
1753
|
+
match
|
|
1754
|
+
)
|
|
1755
|
+
);
|
|
1756
|
+
}
|
|
1757
|
+
for (const bodyName of createResourceNames(options.bodies)) {
|
|
1758
|
+
const match = getCameraFrameResourceMatch(key, bodyName);
|
|
1759
|
+
if (!match) continue;
|
|
1760
|
+
addMountedCameraFrameSourceSuggestion(
|
|
1761
|
+
suggestions,
|
|
1762
|
+
seen,
|
|
1763
|
+
createMountedCameraFrameSourceSuggestion(
|
|
1764
|
+
key,
|
|
1765
|
+
{ bodyName },
|
|
1766
|
+
bodyName,
|
|
1767
|
+
match
|
|
1768
|
+
)
|
|
1769
|
+
);
|
|
1770
|
+
}
|
|
1771
|
+
return suggestions;
|
|
1772
|
+
}
|
|
1630
1773
|
function resolveMountedCameraFrameSource(key, options) {
|
|
1631
1774
|
const cameraNames = createNameSet(options.cameras);
|
|
1632
1775
|
const siteNames = createNameSet(options.sites);
|
|
@@ -1646,6 +1789,14 @@ function resolveMountedCameraFrameSource(key, options) {
|
|
|
1646
1789
|
if (!source) continue;
|
|
1647
1790
|
return { key, selector, source };
|
|
1648
1791
|
}
|
|
1792
|
+
const [suggestion] = createMountedCameraFrameSourceSuggestions(key, options);
|
|
1793
|
+
if (suggestion) {
|
|
1794
|
+
return {
|
|
1795
|
+
key,
|
|
1796
|
+
selector: suggestion.selector,
|
|
1797
|
+
source: suggestion.source
|
|
1798
|
+
};
|
|
1799
|
+
}
|
|
1649
1800
|
if (options.allowAliasFallback) {
|
|
1650
1801
|
for (const selector of aliasCandidates) {
|
|
1651
1802
|
const source = getMountedCameraFrameCaptureSource(selector);
|
|
@@ -1716,6 +1867,77 @@ function createMountedCameraFrameSequenceReadiness(plan) {
|
|
|
1716
1867
|
message: ready ? `All ${plan.cameraKeys.length} requested camera stream${plan.cameraKeys.length === 1 ? "" : "s"} resolve to mounted MuJoCo sources.` : `Missing mounted MuJoCo source${missingKeys.length === 1 ? "" : "s"} for ${missingKeys.join(", ")}.`
|
|
1717
1868
|
};
|
|
1718
1869
|
}
|
|
1870
|
+
function normalizeFrameCount(frameCount) {
|
|
1871
|
+
return Number.isFinite(frameCount) && frameCount !== void 0 ? Math.max(0, Math.floor(frameCount)) : 0;
|
|
1872
|
+
}
|
|
1873
|
+
function createMountedCameraFrameSequenceManifest(result, options = {}) {
|
|
1874
|
+
const cameraKeys = [
|
|
1875
|
+
...options.cameraKeys ?? result.readiness.cameraKeys ?? result.plan.cameraKeys ?? result.cameraKeys
|
|
1876
|
+
];
|
|
1877
|
+
const expectedFrameCount = normalizeFrameCount(
|
|
1878
|
+
options.expectedFrameCount ?? result.frameCount
|
|
1879
|
+
);
|
|
1880
|
+
const recordedFrameCount = normalizeFrameCount(result.frameCount);
|
|
1881
|
+
const streamSummaries = {};
|
|
1882
|
+
const streams = [];
|
|
1883
|
+
let missingFrameCount = 0;
|
|
1884
|
+
let completeStreamCount = 0;
|
|
1885
|
+
let resolvedOrRecordedStreamCount = 0;
|
|
1886
|
+
for (const key of cameraKeys) {
|
|
1887
|
+
const summary = result.cameraSummaries[key];
|
|
1888
|
+
const readiness = result.readiness.cameras[key];
|
|
1889
|
+
const source = summary?.source ?? readiness?.source;
|
|
1890
|
+
const ready = readiness?.ready ?? Boolean(summary);
|
|
1891
|
+
const recorded = normalizeFrameCount(summary?.frameCount);
|
|
1892
|
+
const missing = Math.max(expectedFrameCount - recorded, 0);
|
|
1893
|
+
const complete2 = ready && missing === 0;
|
|
1894
|
+
const status2 = complete2 ? MountedCameraFrameSequenceManifestStatus.Complete : ready || recorded > 0 ? MountedCameraFrameSequenceManifestStatus.Partial : MountedCameraFrameSequenceManifestStatus.Missing;
|
|
1895
|
+
const target = source ? getCameraFrameCaptureSourceTarget(source) : readiness?.message ? void 0 : "missing MuJoCo camera";
|
|
1896
|
+
const message = complete2 ? `Camera stream "${key}" recorded ${recorded} of ${expectedFrameCount} frame${expectedFrameCount === 1 ? "" : "s"}.` : ready || recorded > 0 ? `Camera stream "${key}" recorded ${recorded} of ${expectedFrameCount} frame${expectedFrameCount === 1 ? "" : "s"}.` : readiness?.message ?? `Camera stream "${key}" did not record any frames.`;
|
|
1897
|
+
const stream = {
|
|
1898
|
+
key,
|
|
1899
|
+
ready,
|
|
1900
|
+
complete: complete2,
|
|
1901
|
+
status: status2,
|
|
1902
|
+
source,
|
|
1903
|
+
selector: readiness?.selector,
|
|
1904
|
+
target,
|
|
1905
|
+
width: summary?.width,
|
|
1906
|
+
height: summary?.height,
|
|
1907
|
+
expectedFrameCount,
|
|
1908
|
+
recordedFrameCount: recorded,
|
|
1909
|
+
missingFrameCount: missing,
|
|
1910
|
+
firstFrameIndex: summary?.firstFrameIndex ?? null,
|
|
1911
|
+
lastFrameIndex: summary?.lastFrameIndex ?? null,
|
|
1912
|
+
firstTimestamp: summary?.firstTimestamp ?? null,
|
|
1913
|
+
lastTimestamp: summary?.lastTimestamp ?? null,
|
|
1914
|
+
message
|
|
1915
|
+
};
|
|
1916
|
+
streamSummaries[key] = stream;
|
|
1917
|
+
streams.push(stream);
|
|
1918
|
+
missingFrameCount += missing;
|
|
1919
|
+
if (complete2) completeStreamCount += 1;
|
|
1920
|
+
if (ready || recorded > 0) resolvedOrRecordedStreamCount += 1;
|
|
1921
|
+
}
|
|
1922
|
+
const complete = result.readiness.ready && streams.length === completeStreamCount && missingFrameCount === 0;
|
|
1923
|
+
const status = complete ? MountedCameraFrameSequenceManifestStatus.Complete : resolvedOrRecordedStreamCount > 0 ? MountedCameraFrameSequenceManifestStatus.Partial : MountedCameraFrameSequenceManifestStatus.Missing;
|
|
1924
|
+
return {
|
|
1925
|
+
schema: "mujoco-react/mounted-camera-frame-sequence-manifest@1",
|
|
1926
|
+
ready: result.readiness.ready,
|
|
1927
|
+
complete,
|
|
1928
|
+
status,
|
|
1929
|
+
cameraKeys,
|
|
1930
|
+
resolvedKeys: [...result.readiness.resolvedKeys],
|
|
1931
|
+
missingKeys: [...result.readiness.missingKeys],
|
|
1932
|
+
expectedFrameCount,
|
|
1933
|
+
recordedFrameCount,
|
|
1934
|
+
missingFrameCount,
|
|
1935
|
+
streamSummaries,
|
|
1936
|
+
streams,
|
|
1937
|
+
readiness: result.readiness,
|
|
1938
|
+
message: complete ? `All ${cameraKeys.length} camera stream${cameraKeys.length === 1 ? "" : "s"} recorded ${expectedFrameCount} frame${expectedFrameCount === 1 ? "" : "s"}.` : `Mounted camera sequence coverage is ${status}.`
|
|
1939
|
+
};
|
|
1940
|
+
}
|
|
1719
1941
|
function createMountedCameraFrameSequencePlanFromApi(api, cameraKeys, options = {}) {
|
|
1720
1942
|
return createMountedCameraFrameSequencePlan(cameraKeys, {
|
|
1721
1943
|
...options,
|
|
@@ -2948,7 +3170,7 @@ function MujocoSimProvider({
|
|
|
2948
3170
|
}
|
|
2949
3171
|
const frame = {
|
|
2950
3172
|
frameIndex,
|
|
2951
|
-
time:
|
|
3173
|
+
time: data.time,
|
|
2952
3174
|
cameras: cameraFrames
|
|
2953
3175
|
};
|
|
2954
3176
|
if (retainFrames) {
|
|
@@ -4345,6 +4567,231 @@ function SceneLights({ intensity = 1 }) {
|
|
|
4345
4567
|
useSceneLights(intensity);
|
|
4346
4568
|
return null;
|
|
4347
4569
|
}
|
|
4570
|
+
function SplatCollisionProxyPreview({
|
|
4571
|
+
collisionProxy,
|
|
4572
|
+
xmlText,
|
|
4573
|
+
fetchXml = fetchSplatCollisionProxyXml,
|
|
4574
|
+
color = "#60a5fa",
|
|
4575
|
+
opacity = 0.12,
|
|
4576
|
+
planeColor = "#94a3b8",
|
|
4577
|
+
planeOpacity = 0.08,
|
|
4578
|
+
children,
|
|
4579
|
+
...groupProps
|
|
4580
|
+
}) {
|
|
4581
|
+
const { geoms } = useSplatCollisionProxyGeoms({
|
|
4582
|
+
collisionProxy,
|
|
4583
|
+
xmlText,
|
|
4584
|
+
fetchXml
|
|
4585
|
+
});
|
|
4586
|
+
if (geoms.length === 0 && !children) return null;
|
|
4587
|
+
return /* @__PURE__ */ jsxs(
|
|
4588
|
+
"group",
|
|
4589
|
+
{
|
|
4590
|
+
...groupProps,
|
|
4591
|
+
userData: {
|
|
4592
|
+
kind: "splat-collision-proxy-preview",
|
|
4593
|
+
...groupProps.userData
|
|
4594
|
+
},
|
|
4595
|
+
children: [
|
|
4596
|
+
geoms.map((geom) => /* @__PURE__ */ jsx(
|
|
4597
|
+
SplatCollisionProxyGeom,
|
|
4598
|
+
{
|
|
4599
|
+
geom,
|
|
4600
|
+
color,
|
|
4601
|
+
opacity,
|
|
4602
|
+
planeColor,
|
|
4603
|
+
planeOpacity
|
|
4604
|
+
},
|
|
4605
|
+
geom.id
|
|
4606
|
+
)),
|
|
4607
|
+
children
|
|
4608
|
+
]
|
|
4609
|
+
}
|
|
4610
|
+
);
|
|
4611
|
+
}
|
|
4612
|
+
function useSplatCollisionProxyGeoms({
|
|
4613
|
+
collisionProxy,
|
|
4614
|
+
xmlText,
|
|
4615
|
+
fetchXml = fetchSplatCollisionProxyXml,
|
|
4616
|
+
enabled = true
|
|
4617
|
+
}) {
|
|
4618
|
+
const [loadedXmlText, setLoadedXmlText] = useState(null);
|
|
4619
|
+
const [status, setStatus] = useState("idle");
|
|
4620
|
+
const [error, setError] = useState(null);
|
|
4621
|
+
const xmlPath = collisionProxy?.xmlPath;
|
|
4622
|
+
useEffect(() => {
|
|
4623
|
+
let cancelled = false;
|
|
4624
|
+
if (!enabled) {
|
|
4625
|
+
setLoadedXmlText(null);
|
|
4626
|
+
setStatus("idle");
|
|
4627
|
+
setError(null);
|
|
4628
|
+
return void 0;
|
|
4629
|
+
}
|
|
4630
|
+
if (xmlText) {
|
|
4631
|
+
setLoadedXmlText(xmlText);
|
|
4632
|
+
setStatus("ready");
|
|
4633
|
+
setError(null);
|
|
4634
|
+
return void 0;
|
|
4635
|
+
}
|
|
4636
|
+
if (!xmlPath || !canFetchSplatCollisionProxyXml(xmlPath)) {
|
|
4637
|
+
setLoadedXmlText(null);
|
|
4638
|
+
setStatus("idle");
|
|
4639
|
+
setError(null);
|
|
4640
|
+
return void 0;
|
|
4641
|
+
}
|
|
4642
|
+
const fetchPath = xmlPath;
|
|
4643
|
+
async function loadProxyXml() {
|
|
4644
|
+
setStatus("loading");
|
|
4645
|
+
setError(null);
|
|
4646
|
+
try {
|
|
4647
|
+
const nextXmlText = await fetchXml(fetchPath);
|
|
4648
|
+
if (!cancelled) {
|
|
4649
|
+
setLoadedXmlText(nextXmlText);
|
|
4650
|
+
setStatus("ready");
|
|
4651
|
+
}
|
|
4652
|
+
} catch (nextError) {
|
|
4653
|
+
if (!cancelled) {
|
|
4654
|
+
setLoadedXmlText(null);
|
|
4655
|
+
setStatus("error");
|
|
4656
|
+
setError(
|
|
4657
|
+
nextError instanceof Error ? nextError : new Error("Unable to load collision proxy XML.")
|
|
4658
|
+
);
|
|
4659
|
+
}
|
|
4660
|
+
}
|
|
4661
|
+
}
|
|
4662
|
+
void loadProxyXml();
|
|
4663
|
+
return () => {
|
|
4664
|
+
cancelled = true;
|
|
4665
|
+
};
|
|
4666
|
+
}, [enabled, fetchXml, xmlPath, xmlText]);
|
|
4667
|
+
const geoms = useMemo(
|
|
4668
|
+
() => loadedXmlText ? parseSplatCollisionProxyGeoms(loadedXmlText) : [],
|
|
4669
|
+
[loadedXmlText]
|
|
4670
|
+
);
|
|
4671
|
+
return useMemo(
|
|
4672
|
+
() => ({
|
|
4673
|
+
geoms,
|
|
4674
|
+
status,
|
|
4675
|
+
error,
|
|
4676
|
+
xmlPath
|
|
4677
|
+
}),
|
|
4678
|
+
[error, geoms, status, xmlPath]
|
|
4679
|
+
);
|
|
4680
|
+
}
|
|
4681
|
+
async function fetchSplatCollisionProxyXml(xmlPath) {
|
|
4682
|
+
const response = await fetch(xmlPath);
|
|
4683
|
+
if (!response.ok) {
|
|
4684
|
+
throw new Error(`Unable to load collision proxy XML (${response.status}).`);
|
|
4685
|
+
}
|
|
4686
|
+
return response.text();
|
|
4687
|
+
}
|
|
4688
|
+
function canFetchSplatCollisionProxyXml(xmlPath) {
|
|
4689
|
+
return xmlPath.startsWith("/") || xmlPath.startsWith("http://") || xmlPath.startsWith("https://");
|
|
4690
|
+
}
|
|
4691
|
+
function parseSplatCollisionProxyGeoms(xmlText) {
|
|
4692
|
+
const parser = typeof DOMParser === "undefined" ? null : new DOMParser();
|
|
4693
|
+
if (!parser) return [];
|
|
4694
|
+
const document2 = parser.parseFromString(xmlText, "application/xml");
|
|
4695
|
+
if (document2.querySelector("parsererror")) return [];
|
|
4696
|
+
const bodyPositions = /* @__PURE__ */ new Map();
|
|
4697
|
+
for (const body of Array.from(document2.querySelectorAll("body"))) {
|
|
4698
|
+
const parentBody = body.parentElement?.closest("body");
|
|
4699
|
+
const parentPosition = parentBody ? bodyPositions.get(parentBody) ?? [0, 0, 0] : [0, 0, 0];
|
|
4700
|
+
bodyPositions.set(
|
|
4701
|
+
body,
|
|
4702
|
+
addProxyVectors(parentPosition, parseProxyVector(body.getAttribute("pos")))
|
|
4703
|
+
);
|
|
4704
|
+
}
|
|
4705
|
+
return Array.from(document2.querySelectorAll("geom")).map((geom, index) => {
|
|
4706
|
+
const type = getCollisionProxyGeomType(geom);
|
|
4707
|
+
if (!type) return null;
|
|
4708
|
+
const parentBody = geom.closest("body");
|
|
4709
|
+
const bodyPosition = parentBody ? bodyPositions.get(parentBody) ?? [0, 0, 0] : [0, 0, 0];
|
|
4710
|
+
const position = addProxyVectors(
|
|
4711
|
+
bodyPosition,
|
|
4712
|
+
parseProxyVector(geom.getAttribute("pos"))
|
|
4713
|
+
);
|
|
4714
|
+
const size = parseNumberList(geom.getAttribute("size"));
|
|
4715
|
+
return {
|
|
4716
|
+
id: geom.getAttribute("name") ?? `${type}-${index}`,
|
|
4717
|
+
type,
|
|
4718
|
+
position,
|
|
4719
|
+
size
|
|
4720
|
+
};
|
|
4721
|
+
}).filter((geom) => Boolean(geom));
|
|
4722
|
+
}
|
|
4723
|
+
function SplatCollisionProxyGeom({
|
|
4724
|
+
geom,
|
|
4725
|
+
color,
|
|
4726
|
+
opacity,
|
|
4727
|
+
planeColor,
|
|
4728
|
+
planeOpacity
|
|
4729
|
+
}) {
|
|
4730
|
+
if (geom.type === "sphere") {
|
|
4731
|
+
return /* @__PURE__ */ jsxs("mesh", { position: geom.position, children: [
|
|
4732
|
+
/* @__PURE__ */ jsx("sphereGeometry", { args: [geom.size[0] ?? 0.1, 16, 8] }),
|
|
4733
|
+
/* @__PURE__ */ jsx(SplatCollisionProxyMaterial, { color, opacity })
|
|
4734
|
+
] });
|
|
4735
|
+
}
|
|
4736
|
+
if (geom.type === "plane") {
|
|
4737
|
+
const width = geom.size[0] && geom.size[0] > 0 ? geom.size[0] * 2 : 4;
|
|
4738
|
+
const height = geom.size[1] && geom.size[1] > 0 ? geom.size[1] * 2 : 4;
|
|
4739
|
+
return /* @__PURE__ */ jsxs("mesh", { position: geom.position, children: [
|
|
4740
|
+
/* @__PURE__ */ jsx("boxGeometry", { args: [width, height, 0.02] }),
|
|
4741
|
+
/* @__PURE__ */ jsx(SplatCollisionProxyMaterial, { color: planeColor, opacity: planeOpacity })
|
|
4742
|
+
] });
|
|
4743
|
+
}
|
|
4744
|
+
const size = getCollisionProxyBoxSize(geom);
|
|
4745
|
+
return /* @__PURE__ */ jsxs("mesh", { position: geom.position, children: [
|
|
4746
|
+
/* @__PURE__ */ jsx("boxGeometry", { args: size }),
|
|
4747
|
+
/* @__PURE__ */ jsx(SplatCollisionProxyMaterial, { color, opacity })
|
|
4748
|
+
] });
|
|
4749
|
+
}
|
|
4750
|
+
function SplatCollisionProxyMaterial({
|
|
4751
|
+
color,
|
|
4752
|
+
opacity
|
|
4753
|
+
}) {
|
|
4754
|
+
return /* @__PURE__ */ jsx(
|
|
4755
|
+
"meshBasicMaterial",
|
|
4756
|
+
{
|
|
4757
|
+
color,
|
|
4758
|
+
transparent: true,
|
|
4759
|
+
opacity,
|
|
4760
|
+
wireframe: true
|
|
4761
|
+
}
|
|
4762
|
+
);
|
|
4763
|
+
}
|
|
4764
|
+
function getCollisionProxyGeomType(geom) {
|
|
4765
|
+
const type = geom.getAttribute("type") ?? "sphere";
|
|
4766
|
+
if (type === "box" || type === "plane" || type === "sphere" || type === "capsule" || type === "mesh") {
|
|
4767
|
+
return type;
|
|
4768
|
+
}
|
|
4769
|
+
return null;
|
|
4770
|
+
}
|
|
4771
|
+
function getCollisionProxyBoxSize(geom) {
|
|
4772
|
+
if (geom.type === "capsule") {
|
|
4773
|
+
const radius = geom.size[0] ?? 0.05;
|
|
4774
|
+
const halfLength = geom.size[1] ?? radius;
|
|
4775
|
+
return [radius * 2, radius * 2, Math.max(radius * 2, halfLength * 2)];
|
|
4776
|
+
}
|
|
4777
|
+
if (geom.type === "mesh") return [0.2, 0.2, 0.2];
|
|
4778
|
+
return [
|
|
4779
|
+
(geom.size[0] ?? 0.1) * 2,
|
|
4780
|
+
(geom.size[1] ?? geom.size[0] ?? 0.1) * 2,
|
|
4781
|
+
(geom.size[2] ?? geom.size[0] ?? 0.1) * 2
|
|
4782
|
+
];
|
|
4783
|
+
}
|
|
4784
|
+
function parseProxyVector(value) {
|
|
4785
|
+
const values = parseNumberList(value);
|
|
4786
|
+
return [values[0] ?? 0, values[1] ?? 0, values[2] ?? 0];
|
|
4787
|
+
}
|
|
4788
|
+
function parseNumberList(value) {
|
|
4789
|
+
if (!value) return [];
|
|
4790
|
+
return value.trim().split(/\s+/).map((part) => Number(part)).filter((part) => Number.isFinite(part));
|
|
4791
|
+
}
|
|
4792
|
+
function addProxyVectors(a, b) {
|
|
4793
|
+
return [a[0] + b[0], a[1] + b[1], a[2] + b[2]];
|
|
4794
|
+
}
|
|
4348
4795
|
var JOINT_COLORS = {
|
|
4349
4796
|
0: 16711680,
|
|
4350
4797
|
// free - red
|
|
@@ -5923,22 +6370,46 @@ function useMountedCameraSequenceRecorder(defaultOptions = {}) {
|
|
|
5923
6370
|
const mujoco = useMujoco();
|
|
5924
6371
|
const [status, setStatus] = useState("idle");
|
|
5925
6372
|
const [error, setError] = useState(null);
|
|
6373
|
+
const [plan, setPlan] = useState(null);
|
|
6374
|
+
const [readiness, setReadiness] = useState(null);
|
|
6375
|
+
const [result, setResult] = useState(
|
|
6376
|
+
null
|
|
6377
|
+
);
|
|
5926
6378
|
const reset = useCallback(() => {
|
|
5927
6379
|
setStatus("idle");
|
|
5928
6380
|
setError(null);
|
|
6381
|
+
setPlan(null);
|
|
6382
|
+
setReadiness(null);
|
|
6383
|
+
setResult(null);
|
|
5929
6384
|
}, []);
|
|
5930
6385
|
const createPlan = useCallback(
|
|
5931
6386
|
(cameraKeys, options = {}) => {
|
|
5932
6387
|
if (!mujoco.api) {
|
|
5933
6388
|
throw new Error("MuJoCo scene is not ready for mounted camera sequence planning.");
|
|
5934
6389
|
}
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
6390
|
+
const nextPlan = createMountedCameraFrameSequencePlanFromApi(
|
|
6391
|
+
mujoco.api,
|
|
6392
|
+
cameraKeys,
|
|
6393
|
+
{
|
|
6394
|
+
...defaultOptions,
|
|
6395
|
+
...options
|
|
6396
|
+
}
|
|
6397
|
+
);
|
|
6398
|
+
setPlan(nextPlan);
|
|
6399
|
+
setReadiness(null);
|
|
6400
|
+
return nextPlan;
|
|
5939
6401
|
},
|
|
5940
6402
|
[defaultOptions, mujoco.api]
|
|
5941
6403
|
);
|
|
6404
|
+
const checkReadiness = useCallback(
|
|
6405
|
+
(cameraKeys, options = {}) => {
|
|
6406
|
+
const nextPlan = createPlan(cameraKeys, options);
|
|
6407
|
+
const nextReadiness = createMountedCameraFrameSequenceReadiness(nextPlan);
|
|
6408
|
+
setReadiness(nextReadiness);
|
|
6409
|
+
return nextReadiness;
|
|
6410
|
+
},
|
|
6411
|
+
[createPlan]
|
|
6412
|
+
);
|
|
5942
6413
|
const record = useCallback(
|
|
5943
6414
|
async (options) => {
|
|
5944
6415
|
if (!mujoco.api) {
|
|
@@ -5946,13 +6417,17 @@ function useMountedCameraSequenceRecorder(defaultOptions = {}) {
|
|
|
5946
6417
|
}
|
|
5947
6418
|
setStatus("capturing");
|
|
5948
6419
|
setError(null);
|
|
6420
|
+
setResult(null);
|
|
5949
6421
|
try {
|
|
5950
|
-
const
|
|
6422
|
+
const nextResult = await recordMountedCameraFrameSequence(mujoco.api, {
|
|
5951
6423
|
...defaultOptions,
|
|
5952
6424
|
...options
|
|
5953
6425
|
});
|
|
6426
|
+
setPlan(nextResult.plan);
|
|
6427
|
+
setReadiness(nextResult.readiness);
|
|
6428
|
+
setResult(nextResult);
|
|
5954
6429
|
setStatus("captured");
|
|
5955
|
-
return
|
|
6430
|
+
return nextResult;
|
|
5956
6431
|
} catch (nextError) {
|
|
5957
6432
|
const error2 = nextError instanceof Error ? nextError : new Error("Unable to record the requested mounted camera sequence.");
|
|
5958
6433
|
setError(error2);
|
|
@@ -5965,8 +6440,12 @@ function useMountedCameraSequenceRecorder(defaultOptions = {}) {
|
|
|
5965
6440
|
return {
|
|
5966
6441
|
status,
|
|
5967
6442
|
error,
|
|
6443
|
+
plan,
|
|
6444
|
+
readiness,
|
|
6445
|
+
result,
|
|
5968
6446
|
isRecording: status === "capturing",
|
|
5969
6447
|
createPlan,
|
|
6448
|
+
checkReadiness,
|
|
5970
6449
|
record,
|
|
5971
6450
|
reset
|
|
5972
6451
|
};
|
|
@@ -6322,6 +6801,6 @@ function useCameraAnimation() {
|
|
|
6322
6801
|
* useCameraAnimation — composable camera animation hook.
|
|
6323
6802
|
*/
|
|
6324
6803
|
|
|
6325
|
-
export { Body, ContactListener, ContactMarkers, Debug, DragInteraction, FlexRenderer, IkGizmo, InstancedGeomRenderer, MountedCameraFrameSequenceReadinessStatus, MujocoCanvas, MujocoPhysics, MujocoProvider, MujocoSimProvider, SceneLights, TendonRenderer, TrajectoryPlayer, buildObservation, captureCameraFrame, captureCameraFrameBlob, captureFrame, captureFrameBlob, createCameraFrameCaptureSession, createContiguousControlGroup, createController, createControllerHook, createMountedCameraFrameSequencePlan, createMountedCameraFrameSequencePlanFromApi, createMountedCameraFrameSequenceReadiness, findActuatorByName, findBodyByName, findGeomByName, findJointByName, findKeyframeByName, findSensorByName, findSiteByName, findTendonByName, getActuatedJoints, getCameraFrameCaptureSourceTarget, getControlMap, getMountedCameraFrameCaptureSource, getName, isMountedCameraFrameCaptureSource, loadScene, recordMountedCameraFrameSequence, renderCameraFrameToCanvas, resolveControlGroup, resolveMountedCameraFrameSource, useActuators, useAfterPhysicsStep, useBeforePhysicsStep, useBodyMeshes, useBodyState, useCameraAnimation, useCameraFrameCapture, useCameraSequenceRecorder, useContactEvents, useContacts, useCtrl, useCtrlNoise, useFrameCapture, useGamepad, useGravityCompensation, useIkController, useJointState, useKeyboardTeleop, useMountedCameraSequenceRecorder, useMujoco, useMujocoWasm, useObservation, usePolicy, useSceneLights, useSelectionHighlight, useSensor, useSensors, useSitePosition, useTrajectoryPlayer, useTrajectoryRecorder, useVideoRecorder };
|
|
6804
|
+
export { Body, ContactListener, ContactMarkers, Debug, DragInteraction, FlexRenderer, IkGizmo, InstancedGeomRenderer, MountedCameraFrameSequenceManifestStatus, MountedCameraFrameSequenceReadinessStatus, MountedCameraFrameSourceSuggestionMatch, MujocoCanvas, MujocoPhysics, MujocoProvider, MujocoSimProvider, SceneLights, SplatCollisionProxyPreview, TendonRenderer, TrajectoryPlayer, buildObservation, canFetchSplatCollisionProxyXml, captureCameraFrame, captureCameraFrameBlob, captureFrame, captureFrameBlob, createCameraFrameCaptureSession, createContiguousControlGroup, createController, createControllerHook, createMountedCameraFrameSequenceManifest, createMountedCameraFrameSequencePlan, createMountedCameraFrameSequencePlanFromApi, createMountedCameraFrameSequenceReadiness, createMountedCameraFrameSourceSuggestions, fetchSplatCollisionProxyXml, findActuatorByName, findBodyByName, findGeomByName, findJointByName, findKeyframeByName, findSensorByName, findSiteByName, findTendonByName, getActuatedJoints, getCameraFrameCaptureSourceTarget, getControlMap, getMountedCameraFrameCaptureSource, getName, isMountedCameraFrameCaptureSource, loadScene, parseSplatCollisionProxyGeoms, recordMountedCameraFrameSequence, renderCameraFrameToCanvas, resolveControlGroup, resolveMountedCameraFrameSource, useActuators, useAfterPhysicsStep, useBeforePhysicsStep, useBodyMeshes, useBodyState, useCameraAnimation, useCameraFrameCapture, useCameraSequenceRecorder, useContactEvents, useContacts, useCtrl, useCtrlNoise, useFrameCapture, useGamepad, useGravityCompensation, useIkController, useJointState, useKeyboardTeleop, useMountedCameraSequenceRecorder, useMujoco, useMujocoWasm, useObservation, usePolicy, useSceneLights, useSelectionHighlight, useSensor, useSensors, useSitePosition, useSplatCollisionProxyGeoms, useTrajectoryPlayer, useTrajectoryRecorder, useVideoRecorder };
|
|
6326
6805
|
//# sourceMappingURL=index.js.map
|
|
6327
6806
|
//# sourceMappingURL=index.js.map
|