@nebula-spatial/viewer 0.4.3 → 0.4.5
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/CHANGELOG.md +80 -24
- package/README.md +124 -28
- package/USD-VISUAL-MATERIAL-SUPPORT.md +51 -0
- package/dist/assets/types.d.ts +13 -2
- package/dist/{index-BDo6voB2.js → index-CxbZ--5C.js} +2636 -1856
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -1
- package/dist/inspection/types.d.ts +112 -0
- package/dist/mjcf-visual-6_RqDf4z.js +73 -0
- package/dist/mujoco-runtime-C29PXDOl.js +258 -0
- package/dist/plugins.d.ts +5 -1
- package/dist/{session-1duLyM88.js → session-CnlODeFp.js} +2 -2
- package/dist/session-DNyPmQOW.js +795 -0
- package/dist/{stage-BKqg5vNe.js → stage-BPiGudYP.js} +128 -98
- package/dist/types.d.ts +2 -0
- package/dist/ui/types.d.ts +2 -0
- package/dist/urdf-session-CuIZOiCF.js +3238 -0
- package/dist/usd-session-JDfuDgWv.js +1907 -0
- package/dist/xacro-DrBtv-gK.js +1537 -0
- package/package.json +5 -2
- package/dist/mjcf-visual--X39MVO9.js +0 -63
- package/dist/mujoco-runtime-s85kwjo7.js +0 -208
- package/dist/session-DhZXy40-.js +0 -653
- package/dist/urdf-session-C_LAfkkM.js +0 -439
- package/dist/usd-session-zyj46Bcg.js +0 -1192
package/dist/index.d.ts
CHANGED
|
@@ -5,3 +5,4 @@ export type { Disposable, ToolbarItem, ViewerState, ViewerInteractionHooks, View
|
|
|
5
5
|
export { configureViewerText } from './cad/text';
|
|
6
6
|
export type { ViewerTextBuilderOptions } from './cad/text';
|
|
7
7
|
export type { CadBlockModel, CadDocumentHandle, CadDocumentUnits, CadFontResolver, CadFontRequest, CadResolvedFont, CadResolvedTextFont, CadTextWarmupOptions, CadHudModel, CadHudSnapshot, CadInspectorModel, CadInspectorSnapshot, CadLayerModel, CadLayerRow, CadBlockUsageRow, CadSessionPhase, CadWorldBounds, CadScaleRulerState, CadInsertDetails, CadInsertSelection, CadInsertSelectionId, CadTextDetails, CadTextSelection, CadTextSelectionId, CadSelection, CadHit, CadViewerSnapshot, CadLoadProgress, CadViewerErrorCode, CadViewerErrorEvent, CadLoadSource, CadOpenOptions, CadSessionLoadTiming, RenderDebugBreakdownEntry, SplitBlockLayerDetail, CadViewerCapability, ResolveTextFont, CadViewerEventMap, CameraStateStore, Viewer, ViewerEventMap, ViewerEnvironment, ViewerFitViewOptions, ViewerHighlightOptions, ViewerOptions, ViewerPickOptions, ViewerPickResult, ViewerPointerEvent, ViewerObjectOptions, ViewerCameraSnapshot, ViewerOrthographicSnapshot, ViewerOrbitSnapshot, ViewerRenderStats, ViewerDocumentRect, ViewerCadUiKeyboardOptions, ViewerUiOptions, ViewerCadUiParts, ViewerCadUiOptions, ViewerUiPreset, ViewerCadUiStateStore, ViewerUiTheme, ViewerViewMode, CadInteractionMode, CameraChangeReason, ViewerUiTokens, } from './types';
|
|
8
|
+
export type { AssetInspection, UrdfEntityKind, UrdfInspection, UrdfInspectionEntity, UrdfInspectionIssue, UrdfInspectionJoint, UrdfInspectionLink, UrdfInspectionMode, UrdfInspectionNode, UrdfInspectionOverlays, UrdfInspectionRobot, UrdfInspectionSnapshot, UrdfSourceRange } from './inspection/types';
|
package/dist/index.js
CHANGED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
export type UrdfEntityKind = 'robot' | 'link' | 'joint';
|
|
2
|
+
export interface UrdfSourceRange {
|
|
3
|
+
readonly file: string;
|
|
4
|
+
readonly startOffset: number;
|
|
5
|
+
readonly endOffset: number;
|
|
6
|
+
readonly startLine: number;
|
|
7
|
+
readonly startColumn: number;
|
|
8
|
+
readonly endLine: number;
|
|
9
|
+
readonly endColumn: number;
|
|
10
|
+
}
|
|
11
|
+
export interface UrdfInspectionEntity {
|
|
12
|
+
readonly id: string;
|
|
13
|
+
readonly kind: UrdfEntityKind;
|
|
14
|
+
readonly name: string;
|
|
15
|
+
readonly parentId?: string;
|
|
16
|
+
readonly childIds: readonly string[];
|
|
17
|
+
readonly source?: UrdfSourceRange;
|
|
18
|
+
}
|
|
19
|
+
export interface UrdfInspectionLink extends UrdfInspectionEntity {
|
|
20
|
+
readonly kind: 'link';
|
|
21
|
+
readonly mass?: number;
|
|
22
|
+
readonly centerOfMass?: readonly number[];
|
|
23
|
+
/** Authored inertial frame RPY relative to the link frame, in radians. */
|
|
24
|
+
readonly inertialRotationRpy?: readonly number[];
|
|
25
|
+
readonly inertiaTensor?: readonly number[];
|
|
26
|
+
readonly inertiaMass?: number;
|
|
27
|
+
readonly visualCount: number;
|
|
28
|
+
readonly collisionCount: number;
|
|
29
|
+
}
|
|
30
|
+
export interface UrdfInspectionJoint extends UrdfInspectionEntity {
|
|
31
|
+
readonly kind: 'joint';
|
|
32
|
+
readonly jointType: string;
|
|
33
|
+
readonly parentLink: string;
|
|
34
|
+
readonly childLink: string;
|
|
35
|
+
readonly axis: readonly number[];
|
|
36
|
+
readonly originPosition: readonly number[];
|
|
37
|
+
readonly originRotationRpy: readonly number[];
|
|
38
|
+
readonly limits?: {
|
|
39
|
+
readonly lower: number;
|
|
40
|
+
readonly upper: number;
|
|
41
|
+
};
|
|
42
|
+
readonly effort?: number;
|
|
43
|
+
readonly velocity?: number;
|
|
44
|
+
readonly movable: boolean;
|
|
45
|
+
readonly controlLimits?: {
|
|
46
|
+
readonly lower: number;
|
|
47
|
+
readonly upper: number;
|
|
48
|
+
};
|
|
49
|
+
readonly mimic?: {
|
|
50
|
+
readonly joint: string;
|
|
51
|
+
readonly multiplier: number;
|
|
52
|
+
readonly offset: number;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
export interface UrdfInspectionRobot extends UrdfInspectionEntity {
|
|
56
|
+
readonly kind: 'robot';
|
|
57
|
+
readonly rootLinkIds: readonly string[];
|
|
58
|
+
}
|
|
59
|
+
export type UrdfInspectionNode = UrdfInspectionRobot | UrdfInspectionLink | UrdfInspectionJoint;
|
|
60
|
+
export interface UrdfInspectionIssue {
|
|
61
|
+
readonly id: string;
|
|
62
|
+
readonly ruleId: string;
|
|
63
|
+
readonly severity: 'error';
|
|
64
|
+
readonly title: string;
|
|
65
|
+
readonly message: string;
|
|
66
|
+
readonly entityId: string;
|
|
67
|
+
readonly source?: UrdfSourceRange;
|
|
68
|
+
readonly values?: Readonly<Record<string, unknown>>;
|
|
69
|
+
}
|
|
70
|
+
export interface UrdfInspectionOverlays {
|
|
71
|
+
readonly collision: boolean;
|
|
72
|
+
readonly jointAxis: boolean;
|
|
73
|
+
readonly centerOfMass: boolean;
|
|
74
|
+
readonly inertia?: boolean;
|
|
75
|
+
}
|
|
76
|
+
export type UrdfInspectionMode = 'kinematic' | 'dynamics-initializing' | 'dynamics-paused' | 'dynamics-running' | 'dynamics-unavailable';
|
|
77
|
+
export interface UrdfInspectionSnapshot {
|
|
78
|
+
readonly revision: number;
|
|
79
|
+
readonly kind: 'urdf';
|
|
80
|
+
readonly robotName: string;
|
|
81
|
+
readonly sourcePath: string;
|
|
82
|
+
readonly sourceText: string;
|
|
83
|
+
readonly nodes: readonly UrdfInspectionNode[];
|
|
84
|
+
readonly rootEntityId: string;
|
|
85
|
+
readonly selectedEntityId: string | null;
|
|
86
|
+
readonly jointValues: Readonly<Record<string, number>>;
|
|
87
|
+
readonly overlays: UrdfInspectionOverlays;
|
|
88
|
+
readonly issues: readonly UrdfInspectionIssue[];
|
|
89
|
+
readonly mode: UrdfInspectionMode;
|
|
90
|
+
readonly dynamicsAvailable: boolean;
|
|
91
|
+
readonly dynamicsMessage?: string;
|
|
92
|
+
}
|
|
93
|
+
export interface UrdfInspection {
|
|
94
|
+
readonly kind: 'urdf';
|
|
95
|
+
getSnapshot(): UrdfInspectionSnapshot;
|
|
96
|
+
subscribe(listener: (snapshot: UrdfInspectionSnapshot) => void): () => void;
|
|
97
|
+
select(entityId: string | null): void;
|
|
98
|
+
setJointValue(jointId: string, value: number): void;
|
|
99
|
+
resetJoint(jointId: string): void;
|
|
100
|
+
resetPose(): void;
|
|
101
|
+
setOverlay(name: keyof UrdfInspectionOverlays, visible: boolean): void;
|
|
102
|
+
enterKinematicPreview(): void;
|
|
103
|
+
jointWorldFrame?(jointId: string): {
|
|
104
|
+
position: import('three').Vector3;
|
|
105
|
+
axis: import('three').Vector3;
|
|
106
|
+
} | null;
|
|
107
|
+
/** World-space label anchor at the own-link visual bounds center. */
|
|
108
|
+
linkWorldPosition?(linkId: string, target: import('three').Vector3): boolean;
|
|
109
|
+
pickRoots?(): readonly import('three').Object3D[];
|
|
110
|
+
}
|
|
111
|
+
/** Tagged inspection surface. Future formats can extend this union without changing AssetPreview. */
|
|
112
|
+
export type AssetInspection = UrdfInspection;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { e as v, s as E } from "./session-DNyPmQOW.js";
|
|
2
|
+
import { Group as q, Euler as z, BoxGeometry as G, SphereGeometry as N, CylinderGeometry as P, CapsuleGeometry as $, PlaneGeometry as I, Mesh as X } from "three";
|
|
3
|
+
import { d as j } from "./stage-BPiGudYP.js";
|
|
4
|
+
function U(f) {
|
|
5
|
+
const y = f.extension.documents, l = new q();
|
|
6
|
+
l.name = f.name;
|
|
7
|
+
try {
|
|
8
|
+
if (y.size !== 1) throw new Error("MJCF 独立预览暂不支持 include");
|
|
9
|
+
const u = [...y.values()][0];
|
|
10
|
+
if (u.querySelector("default, include, attach, replicate, composite, flexcomp, skin, hfield, plugin")) throw new Error("MJCF 独立预览包含尚不支持的继承、组合或扩展语义");
|
|
11
|
+
const a = u.querySelector("compiler");
|
|
12
|
+
if ((a == null ? void 0 : a.getAttribute("coordinate")) === "global" || a != null && a.hasAttribute("eulerseq")) throw new Error("MJCF 独立预览不支持此坐标设置");
|
|
13
|
+
const g = (a == null ? void 0 : a.getAttribute("angle")) === "radian" ? 1 : Math.PI / 180, b = (c, m) => {
|
|
14
|
+
const e = c ? c.trim().split(/\s+/).map(Number) : m;
|
|
15
|
+
if (e.some((s) => !Number.isFinite(s))) throw new Error("MJCF 视觉坐标无效");
|
|
16
|
+
return e;
|
|
17
|
+
}, h = (c, m, e) => {
|
|
18
|
+
const s = b(c, m);
|
|
19
|
+
if (s.length !== e) throw new Error(`MJCF 视觉变换需要 ${e} 个分量`);
|
|
20
|
+
return s;
|
|
21
|
+
}, M = (c, m) => {
|
|
22
|
+
for (const e of c.children) {
|
|
23
|
+
if (!["body", "geom"].includes(e.tagName)) continue;
|
|
24
|
+
if (["class", "childclass", "fromto", "axisangle", "xyaxes", "zaxis"].some((t) => e.hasAttribute(t))) throw new Error("MJCF 独立预览不支持此变换或默认类");
|
|
25
|
+
const s = new q();
|
|
26
|
+
if (s.name = e.getAttribute("name") ?? e.tagName, s.position.fromArray(h(e.getAttribute("pos"), [0, 0, 0], 3)), e.hasAttribute("quat")) {
|
|
27
|
+
const t = h(e.getAttribute("quat"), [1, 0, 0, 0], 4);
|
|
28
|
+
if (Math.hypot(...t) === 0) throw new Error("MJCF 视觉四元数不能为零");
|
|
29
|
+
s.quaternion.set(t[1], t[2], t[3], t[0]).normalize();
|
|
30
|
+
} else {
|
|
31
|
+
const t = h(e.getAttribute("euler"), [0, 0, 0], 3);
|
|
32
|
+
s.quaternion.setFromEuler(new z(t[0] * g, t[1] * g, t[2] * g, "XYZ"));
|
|
33
|
+
}
|
|
34
|
+
if (m.add(s), e.tagName === "body") {
|
|
35
|
+
M(e, s);
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
const n = e.getAttribute("type") ?? (e.hasAttribute("mesh") ? "mesh" : "sphere"), r = b(e.getAttribute("size"), []);
|
|
39
|
+
if (r.length === 0) throw new Error("MJCF 独立预览缺少可直接解析的几何或材质");
|
|
40
|
+
const F = { box: 3, sphere: 1, cylinder: 2, capsule: 2, plane: 3 }[n];
|
|
41
|
+
if (F && (r.length < F || r.some((t) => n === "plane" ? t < 0 : t <= 0)))
|
|
42
|
+
throw new Error(`MJCF geom ${n} 的尺寸缺失或无效`);
|
|
43
|
+
let i;
|
|
44
|
+
if (n === "box") i = new G(2 * r[0], 2 * r[1], 2 * r[2]);
|
|
45
|
+
else if (n === "sphere") i = new N(r[0]);
|
|
46
|
+
else if (n === "cylinder")
|
|
47
|
+
i = new P(r[0], r[0], 2 * r[1]), i.rotateX(Math.PI / 2);
|
|
48
|
+
else if (n === "capsule")
|
|
49
|
+
i = new $(r[0], 2 * r[1]), i.rotateX(Math.PI / 2);
|
|
50
|
+
else if (n === "plane") i = new I(2 * (r[0] || 5), 2 * (r[1] || 5));
|
|
51
|
+
else throw new Error(`MJCF 独立预览暂不支持 geom ${n}`);
|
|
52
|
+
const p = e.getAttribute("material"), o = [...u.querySelectorAll("asset > material")].find((t) => t.getAttribute("name") === p);
|
|
53
|
+
if (p && !o) throw new Error(`Undefined MJCF material: ${p}`);
|
|
54
|
+
if (o != null && o.hasAttribute("texture") || o != null && o.querySelector("layer")) throw new Error("MJCF textured preview requires the compiled material pipeline");
|
|
55
|
+
const x = h(e.getAttribute("rgba"), [0.5, 0.5, 0.5, 1], 4), J = o && x.every((t, d) => t === [0.5, 0.5, 0.5, 1][d]) ? h(o.getAttribute("rgba"), [1, 1, 1, 1], 4) : x, w = (t, d) => o != null && o.hasAttribute(t) ? b(o.getAttribute(t), [d])[0] : d, S = v({
|
|
56
|
+
color: J,
|
|
57
|
+
roughness: o ? E(w("roughness", -1), w("shininess", 0.5)) : E(),
|
|
58
|
+
metalness: Math.max(0, w("metallic", 0)),
|
|
59
|
+
specular: w("specular", 0.5),
|
|
60
|
+
emission: w("emission", 0)
|
|
61
|
+
}), C = new X(i, S);
|
|
62
|
+
n === "plane" && m === l && (C.userData.excludeFromSimulationFit = !0), s.add(C);
|
|
63
|
+
}
|
|
64
|
+
}, A = u.querySelector("worldbody");
|
|
65
|
+
if (!A) throw new Error("MJCF 缺少 worldbody");
|
|
66
|
+
return M(A, l), l.updateMatrixWorld(!0), l;
|
|
67
|
+
} catch (u) {
|
|
68
|
+
throw j(l), u;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
export {
|
|
72
|
+
U as compileMjcfPreview
|
|
73
|
+
};
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
var M = Object.defineProperty;
|
|
2
|
+
var O = (l, t, i) => t in l ? M(l, t, { enumerable: !0, configurable: !0, writable: !0, value: i }) : l[t] = i;
|
|
3
|
+
var _ = (l, t, i) => O(l, typeof t != "symbol" ? t + "" : t, i);
|
|
4
|
+
import I from "@mujoco/mujoco";
|
|
5
|
+
import T from "@mujoco/mujoco/mujoco.wasm?url";
|
|
6
|
+
let v = 0;
|
|
7
|
+
function G(l, t) {
|
|
8
|
+
const i = `/viewer-simulation-${++v}`;
|
|
9
|
+
l.mkdir(i);
|
|
10
|
+
const n = /* @__PURE__ */ new Set([i]), o = /* @__PURE__ */ new Set(), a = (e, s) => {
|
|
11
|
+
const m = e.replaceAll("\\", "/").split("/");
|
|
12
|
+
if (m[0] === "" || m.some((d) => d === ".." || d.includes(":"))) throw p(`Invalid package path: ${e}`, "E_SIMULATION_PATH");
|
|
13
|
+
const h = m.filter((d) => d && d !== ".");
|
|
14
|
+
if (!h.length) throw p("Package path is empty.", "E_SIMULATION_PATH");
|
|
15
|
+
let c = i;
|
|
16
|
+
for (const d of h.slice(0, -1))
|
|
17
|
+
c += `/${d}`, n.has(c) || (l.mkdir(c), n.add(c));
|
|
18
|
+
const u = `${i}/${h.join("/")}`;
|
|
19
|
+
return l.writeFile(u, s), o.add(u), u;
|
|
20
|
+
}, r = () => {
|
|
21
|
+
for (const e of o) l.unlink(e);
|
|
22
|
+
o.clear();
|
|
23
|
+
for (const e of [...n].reverse()) l.rmdir(e);
|
|
24
|
+
n.clear();
|
|
25
|
+
};
|
|
26
|
+
try {
|
|
27
|
+
for (const [e, s] of t) a(e, s);
|
|
28
|
+
return { writeEntry: (e) => a("__viewer_entry.xml", e), dispose: r };
|
|
29
|
+
} catch (e) {
|
|
30
|
+
throw r(), e;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function p(l, t) {
|
|
34
|
+
return Object.assign(new Error(l), { code: t });
|
|
35
|
+
}
|
|
36
|
+
function w({
|
|
37
|
+
target: l,
|
|
38
|
+
point: t,
|
|
39
|
+
velocity: i,
|
|
40
|
+
stiffness: n,
|
|
41
|
+
damping: o,
|
|
42
|
+
maxForce: a,
|
|
43
|
+
strength: r
|
|
44
|
+
}) {
|
|
45
|
+
const e = [
|
|
46
|
+
n * (l[0] - t[0]) - o * i[0],
|
|
47
|
+
n * (l[1] - t[1]) - o * i[1],
|
|
48
|
+
n * (l[2] - t[2]) - o * i[2]
|
|
49
|
+
], s = Math.hypot(e[0], e[1], e[2]), m = a * r;
|
|
50
|
+
if (s > m && s > 0) {
|
|
51
|
+
const h = m / s;
|
|
52
|
+
e[0] *= h, e[1] *= h, e[2] *= h;
|
|
53
|
+
}
|
|
54
|
+
return e;
|
|
55
|
+
}
|
|
56
|
+
const g = 1 / 240, F = 0.05, x = 80, J = 1.15, b = 3.5, y = 14;
|
|
57
|
+
class S {
|
|
58
|
+
constructor() {
|
|
59
|
+
_(this, "mujoco", null);
|
|
60
|
+
_(this, "model", null);
|
|
61
|
+
_(this, "data", null);
|
|
62
|
+
_(this, "grab", null);
|
|
63
|
+
_(this, "lastGrabForce", [0, 0, 0]);
|
|
64
|
+
_(this, "velocityBuffer", new Float64Array(6));
|
|
65
|
+
_(this, "initialControls", []);
|
|
66
|
+
_(this, "accumulator", 0);
|
|
67
|
+
_(this, "initialGround", null);
|
|
68
|
+
_(this, "initialVelocities", []);
|
|
69
|
+
}
|
|
70
|
+
async initialize(t, i) {
|
|
71
|
+
var e;
|
|
72
|
+
this.mujoco ?? (this.mujoco = await I({ locateFile: (s) => s.endsWith(".wasm") ? i ? new URL(s, i).href : T : s }));
|
|
73
|
+
const n = G(this.mujoco.FS, t.files);
|
|
74
|
+
let o = null, a = null;
|
|
75
|
+
try {
|
|
76
|
+
o = this.mujoco.MjModel.from_xml_path(n.writeEntry(t.xml));
|
|
77
|
+
for (const s of t.massOverrides ?? []) {
|
|
78
|
+
const m = this.mujoco.mj_name2id(o, this.mujoco.mjtObj.mjOBJ_BODY.value, s.body);
|
|
79
|
+
if (m <= 0 || !Number.isFinite(s.mass) || s.mass <= 0 || !(o.body_mass[m] > 0)) throw new Error(`Invalid mass override: ${s.body}`);
|
|
80
|
+
const h = s.mass / o.body_mass[m];
|
|
81
|
+
o.body_mass[m] = s.mass;
|
|
82
|
+
for (let c = 0; c < 3; c++) o.body_inertia[m * 3 + c] *= h;
|
|
83
|
+
}
|
|
84
|
+
a = new this.mujoco.MjData(o), (e = t.massOverrides) != null && e.length && this.mujoco.mj_setConst(o, a), o.opt.timestep = g, this.applyInitialControls(a, t.initialControls), this.applyInitialVelocities(o, a, t.initialVelocities), this.mujoco.mj_forward(o, a), this.seatDefaultGround(o, a);
|
|
85
|
+
} catch (s) {
|
|
86
|
+
throw a == null || a.delete(), o == null || o.delete(), f(s, "E_MUJOCO_INITIALIZE");
|
|
87
|
+
} finally {
|
|
88
|
+
n.dispose();
|
|
89
|
+
}
|
|
90
|
+
this.disposeModel(), this.model = o, this.data = a, this.initialControls = t.initialControls ?? [], this.initialVelocities = t.initialVelocities ?? [], this.accumulator = 0;
|
|
91
|
+
const r = this.mujoco.mj_name2id(o, this.mujoco.mjtObj.mjOBJ_GEOM.value, "__viewer_default_ground__");
|
|
92
|
+
this.initialGround = r < 0 ? null : { id: r, height: o.geom_pos[r * 3 + 2], sameFrame: o.geom_sameframe[r] };
|
|
93
|
+
}
|
|
94
|
+
get defaultGroundHeight() {
|
|
95
|
+
if (!this.mujoco || !this.model) return;
|
|
96
|
+
const t = this.mujoco.mj_name2id(this.model, this.mujoco.mjtObj.mjOBJ_GEOM.value, "__viewer_default_ground__");
|
|
97
|
+
return t < 0 ? void 0 : this.model.geom_pos[t * 3 + 2];
|
|
98
|
+
}
|
|
99
|
+
/** Lower only the viewer-owned plane, never authored bodies or authored floors.
|
|
100
|
+
* Run after forward kinematics, not during stepping (the floor must not follow falling bodies).
|
|
101
|
+
*/
|
|
102
|
+
seatDefaultGround(t, i) {
|
|
103
|
+
const n = this.mujoco, o = n.mj_name2id(t, n.mjtObj.mjOBJ_GEOM.value, "__viewer_default_ground__");
|
|
104
|
+
if (o < 0 || t.geom_bodyid[o] !== 0 || t.geom_type[o] !== n.mjtGeom.mjGEOM_PLANE.value) return;
|
|
105
|
+
let a = t.geom_pos[o * 3 + 2];
|
|
106
|
+
for (let r = 0; r < t.ngeom; r++) {
|
|
107
|
+
if (r === o || t.geom_type[r] === n.mjtGeom.mjGEOM_PLANE.value || !(t.geom_contype[r] & t.geom_conaffinity[o]) && !(t.geom_conaffinity[r] & t.geom_contype[o])) continue;
|
|
108
|
+
const e = r * 6, s = r * 9, m = i.geom_xmat[s + 6], h = i.geom_xmat[s + 7], c = i.geom_xmat[s + 8], u = i.geom_xpos[r * 3 + 2] + m * t.geom_aabb[e] + h * t.geom_aabb[e + 1] + c * t.geom_aabb[e + 2], d = Math.abs(m) * t.geom_aabb[e + 3] + Math.abs(h) * t.geom_aabb[e + 4] + Math.abs(c) * t.geom_aabb[e + 5], j = u - d, E = Math.max(1e-3, t.geom_margin[r] + t.geom_margin[o]);
|
|
109
|
+
Number.isFinite(j) && j < a && (a = Math.min(a, j - E));
|
|
110
|
+
}
|
|
111
|
+
a !== t.geom_pos[o * 3 + 2] && (t.geom_pos[o * 3 + 2] = a, t.geom_sameframe[o] = 0, n.mj_forward(t, i));
|
|
112
|
+
}
|
|
113
|
+
beginGrab({
|
|
114
|
+
bodyId: t,
|
|
115
|
+
localPoint: i,
|
|
116
|
+
target: n,
|
|
117
|
+
stiffness: o = x,
|
|
118
|
+
damping: a,
|
|
119
|
+
maxForce: r,
|
|
120
|
+
dampingRatio: e = J,
|
|
121
|
+
maxTargetSpeed: s = b,
|
|
122
|
+
targetResponse: m = y
|
|
123
|
+
}) {
|
|
124
|
+
var j;
|
|
125
|
+
const h = Math.max(((j = this.model) == null ? void 0 : j.body_mass[t]) ?? 1, 1e-3), c = a ?? 2 * Math.sqrt(o * h) * e, u = r ?? Math.max(120, h * 60), d = [n[0], n[1], n[2]];
|
|
126
|
+
this.grab = {
|
|
127
|
+
bodyId: t,
|
|
128
|
+
localPoint: [i[0], i[1], i[2]],
|
|
129
|
+
target: [...d],
|
|
130
|
+
requestedTarget: d,
|
|
131
|
+
stiffness: o,
|
|
132
|
+
damping: c,
|
|
133
|
+
maxForce: u,
|
|
134
|
+
strength: 1,
|
|
135
|
+
maxTargetSpeed: s,
|
|
136
|
+
targetResponse: m
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
updateGrabTarget(t) {
|
|
140
|
+
this.grab && (this.grab.requestedTarget = [t[0], t[1], t[2]]);
|
|
141
|
+
}
|
|
142
|
+
updateGrabStrength(t) {
|
|
143
|
+
this.grab && (this.grab.strength = t);
|
|
144
|
+
}
|
|
145
|
+
applyGrabForce() {
|
|
146
|
+
if (!this.grab || !this.model || !this.data || !this.mujoco) return;
|
|
147
|
+
const { bodyId: t, localPoint: i, target: n, stiffness: o, damping: a, maxForce: r, strength: e } = this.grab, s = this.data.xmat.subarray(t * 9, t * 9 + 9), m = this.data.xpos.subarray(t * 3, t * 3 + 3), h = [
|
|
148
|
+
m[0] + s[0] * i[0] + s[1] * i[1] + s[2] * i[2],
|
|
149
|
+
m[1] + s[3] * i[0] + s[4] * i[1] + s[5] * i[2],
|
|
150
|
+
m[2] + s[6] * i[0] + s[7] * i[1] + s[8] * i[2]
|
|
151
|
+
];
|
|
152
|
+
this.mujoco.mj_objectVelocity(
|
|
153
|
+
this.model,
|
|
154
|
+
this.data,
|
|
155
|
+
this.mujoco.mjtObj.mjOBJ_BODY.value,
|
|
156
|
+
t,
|
|
157
|
+
this.velocityBuffer,
|
|
158
|
+
0
|
|
159
|
+
);
|
|
160
|
+
const c = this.velocityBuffer, u = [
|
|
161
|
+
h[0] - m[0],
|
|
162
|
+
h[1] - m[1],
|
|
163
|
+
h[2] - m[2]
|
|
164
|
+
], d = [
|
|
165
|
+
c[3] + c[1] * u[2] - c[2] * u[1],
|
|
166
|
+
c[4] + c[2] * u[0] - c[0] * u[2],
|
|
167
|
+
c[5] + c[0] * u[1] - c[1] * u[0]
|
|
168
|
+
], j = w({ target: n, point: h, velocity: d, stiffness: o, damping: a, maxForce: r, strength: e });
|
|
169
|
+
this.lastGrabForce = [...j], this.data.qfrc_applied.fill(0), this.mujoco.mj_applyFT(
|
|
170
|
+
this.model,
|
|
171
|
+
this.data,
|
|
172
|
+
j,
|
|
173
|
+
[0, 0, 0],
|
|
174
|
+
h,
|
|
175
|
+
t,
|
|
176
|
+
this.data.qfrc_applied
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
endGrab() {
|
|
180
|
+
this.data && this.data.qfrc_applied.fill(0), this.lastGrabForce = [0, 0, 0], this.grab = null;
|
|
181
|
+
}
|
|
182
|
+
step(t) {
|
|
183
|
+
if (!this.mujoco || !this.model || !this.data || t <= 0) return !1;
|
|
184
|
+
this.accumulator += Math.min(t, F);
|
|
185
|
+
let i = !1;
|
|
186
|
+
for (; this.accumulator >= g; )
|
|
187
|
+
this.updateGrabTargetForStep(), this.applyGrabForce(), this.mujoco.mj_step(this.model, this.data), this.accumulator -= g, i = !0;
|
|
188
|
+
return i;
|
|
189
|
+
}
|
|
190
|
+
updateGrabTargetForStep() {
|
|
191
|
+
const t = this.grab;
|
|
192
|
+
if (!t) return;
|
|
193
|
+
const i = t.requestedTarget ?? t.target, n = (t.maxTargetSpeed ?? b) * Math.max(1, Math.sqrt(t.strength)), o = t.targetResponse ?? y, a = 1 - Math.exp(-o * g), r = n * g;
|
|
194
|
+
for (let e = 0; e < 3; e++) {
|
|
195
|
+
const s = i[e] - t.target[e], m = Math.sign(s) * Math.min(Math.abs(s) * a, r);
|
|
196
|
+
t.target[e] += m;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
setJointValues(t) {
|
|
200
|
+
if (!(!this.mujoco || !this.model || !this.data)) {
|
|
201
|
+
for (const [i, n] of Object.entries(t)) {
|
|
202
|
+
const o = this.mujoco.mj_name2id(this.model, this.mujoco.mjtObj.mjOBJ_JOINT.value, i);
|
|
203
|
+
if (o < 0 || !Number.isFinite(n)) continue;
|
|
204
|
+
const a = this.model.jnt_type[o], r = this.mujoco.mjtJoint.mjJNT_HINGE.value, e = this.mujoco.mjtJoint.mjJNT_SLIDE.value;
|
|
205
|
+
a !== r && a !== e || (this.data.qpos[this.model.jnt_qposadr[o]] = n);
|
|
206
|
+
}
|
|
207
|
+
this.mujoco.mj_forward(this.model, this.data), this.seatDefaultGround(this.model, this.data), this.accumulator = 0;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
getJointValues(t) {
|
|
211
|
+
const i = {};
|
|
212
|
+
if (!this.mujoco || !this.model || !this.data) return i;
|
|
213
|
+
for (const n of t) {
|
|
214
|
+
const o = this.mujoco.mj_name2id(this.model, this.mujoco.mjtObj.mjOBJ_JOINT.value, n);
|
|
215
|
+
if (o < 0) continue;
|
|
216
|
+
const a = this.model.jnt_type[o], r = this.mujoco.mjtJoint.mjJNT_HINGE.value, e = this.mujoco.mjtJoint.mjJNT_SLIDE.value;
|
|
217
|
+
(a === r || a === e) && (i[n] = this.data.qpos[this.model.jnt_qposadr[o]]);
|
|
218
|
+
}
|
|
219
|
+
return i;
|
|
220
|
+
}
|
|
221
|
+
reset() {
|
|
222
|
+
if (!(!this.mujoco || !this.model || !this.data)) {
|
|
223
|
+
if (this.endGrab(), this.initialGround) {
|
|
224
|
+
const { id: t, height: i, sameFrame: n } = this.initialGround;
|
|
225
|
+
this.model.geom_pos[t * 3 + 2] = i, this.model.geom_sameframe[t] = n;
|
|
226
|
+
}
|
|
227
|
+
this.mujoco.mj_resetData(this.model, this.data), this.applyInitialControls(this.data, this.initialControls), this.applyInitialVelocities(this.model, this.data, this.initialVelocities), this.mujoco.mj_forward(this.model, this.data), this.accumulator = 0;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
dispose() {
|
|
231
|
+
this.disposeModel(), this.mujoco = null;
|
|
232
|
+
}
|
|
233
|
+
applyInitialControls(t, i) {
|
|
234
|
+
if (i != null && i.length) {
|
|
235
|
+
if (i.length !== t.ctrl.length) throw f(new Error(`Initial control count ${i.length} does not match MuJoCo actuator count ${t.ctrl.length}.`), "E_MUJOCO_INITIALIZE");
|
|
236
|
+
t.ctrl.set(i);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
applyInitialVelocities(t, i, n) {
|
|
240
|
+
for (const o of n ?? []) {
|
|
241
|
+
const a = this.mujoco.mj_name2id(t, this.mujoco.mjtObj.mjOBJ_BODY.value, o.body), r = a >= 0 ? t.body_jntadr[a] : -1;
|
|
242
|
+
if (r < 0 || t.jnt_type[r] !== this.mujoco.mjtJoint.mjJNT_FREE.value || o.velocity.length !== 6 || !o.velocity.every(Number.isFinite))
|
|
243
|
+
throw f(new Error(`Invalid free-body initial velocity: ${o.body}`), "E_MUJOCO_INITIALIZE");
|
|
244
|
+
i.qvel.set(o.velocity, t.jnt_dofadr[r]);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
disposeModel() {
|
|
248
|
+
var t, i;
|
|
249
|
+
this.endGrab(), (t = this.data) == null || t.delete(), (i = this.model) == null || i.delete(), this.data = null, this.model = null, this.initialGround = null, this.initialControls = [], this.initialVelocities = [], this.accumulator = 0;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
function f(l, t) {
|
|
253
|
+
const i = l instanceof Error ? l : new Error(String(l));
|
|
254
|
+
return Object.assign(i, { code: t });
|
|
255
|
+
}
|
|
256
|
+
export {
|
|
257
|
+
S as MujocoRuntime
|
|
258
|
+
};
|
package/dist/plugins.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { Box3, Object3D } from 'three';
|
|
|
2
2
|
import type { CadHit, CadSelection, CadViewerSnapshot, CadViewerEventMap } from './cad/types';
|
|
3
3
|
import type { ViewerEventMap, ViewerViewMode, ViewerUiTheme } from './types';
|
|
4
4
|
import type { AssetControlState, AssetLoaderContribution, AssetPreviewSnapshot, AssetUpAxis } from './assets/types';
|
|
5
|
+
import type { AssetInspection } from './inspection/types';
|
|
5
6
|
export interface Disposable {
|
|
6
7
|
dispose(): void;
|
|
7
8
|
}
|
|
@@ -27,6 +28,7 @@ export interface ViewerAssetReadApi {
|
|
|
27
28
|
$read(): AssetPreviewSnapshot | null;
|
|
28
29
|
/** Authoritative control state, safe to bind button pressed states to. */
|
|
29
30
|
$readControl(): AssetControlState;
|
|
31
|
+
$inspection(): AssetInspection | null;
|
|
30
32
|
}
|
|
31
33
|
export interface ViewerPluginContext {
|
|
32
34
|
readonly cad: {
|
|
@@ -112,6 +114,7 @@ type SnapshotReader = () => CadViewerSnapshot;
|
|
|
112
114
|
type StateReader = () => ViewerState;
|
|
113
115
|
type AssetReader = () => AssetPreviewSnapshot | null;
|
|
114
116
|
type ControlStateReader = () => AssetControlState;
|
|
117
|
+
type InspectionReader = () => AssetInspection | null;
|
|
115
118
|
type PluginEventMap = ViewerEventMap & CadViewerEventMap;
|
|
116
119
|
type EventSubscriber = <K extends keyof PluginEventMap>(type: K, listener: (event: PluginEventMap[K]) => void) => () => void;
|
|
117
120
|
type CommandApi = ViewerPluginContext['commands'];
|
|
@@ -137,6 +140,7 @@ export declare class ViewerPluginManager implements ViewerPluginRegistry {
|
|
|
137
140
|
private readonly commands;
|
|
138
141
|
private readonly hostElement;
|
|
139
142
|
private readonly layout;
|
|
143
|
+
private readonly readInspection;
|
|
140
144
|
private readonly pluginsById;
|
|
141
145
|
private readonly contributions;
|
|
142
146
|
private readonly commandContributions;
|
|
@@ -144,7 +148,7 @@ export declare class ViewerPluginManager implements ViewerPluginRegistry {
|
|
|
144
148
|
private readonly contributionRemovedListeners;
|
|
145
149
|
private refreshingInspectors;
|
|
146
150
|
private disposed;
|
|
147
|
-
constructor(readCad: SnapshotReader, readState: StateReader, readAsset: AssetReader, readControlState: ControlStateReader, subscribe: EventSubscriber, commands: CommandApi, hostElement: HTMLElement, layout?: PluginLayoutPort | null);
|
|
151
|
+
constructor(readCad: SnapshotReader, readState: StateReader, readAsset: AssetReader, readControlState: ControlStateReader, subscribe: EventSubscriber, commands: CommandApi, hostElement: HTMLElement, layout?: PluginLayoutPort | null, readInspection?: InspectionReader);
|
|
148
152
|
register(plugin: ViewerPlugin): Disposable;
|
|
149
153
|
use(plugins: readonly ViewerPlugin[], options?: {
|
|
150
154
|
replace?: boolean;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { DataTexture as N, LinearMipmapLinearFilter as oe, LinearFilter as ae, LoadingManager as ie, Loader as le, Texture as X, TextureLoader as ce, LinearSRGBColorSpace as G, SRGBColorSpace as ue, RGBAFormat as K, UnsignedByteType as J, NoColorSpace as fe, Mesh as pe, MeshPhysicalMaterial as Q, MeshPhongMaterial as k, MeshLambertMaterial as de, Color as _, Quaternion as Z, Vector3 as ee, Group as he, Box3 as me } from "three";
|
|
2
2
|
import { DRACOLoader as ge } from "three/examples/jsm/loaders/DRACOLoader.js";
|
|
3
3
|
import { GLTFLoader as ye } from "three/examples/jsm/loaders/GLTFLoader.js";
|
|
4
|
-
import { S as we, d as Y } from "./stage-
|
|
4
|
+
import { S as we, d as Y } from "./stage-BPiGudYP.js";
|
|
5
5
|
import { TGALoader as be } from "three/examples/jsm/loaders/TGALoader.js";
|
|
6
6
|
import { FBXLoader as xe } from "three/examples/jsm/loaders/FBXLoader.js";
|
|
7
7
|
import { unzlibSync as Ee } from "three/examples/jsm/libs/fflate.module.js";
|
|
@@ -177,7 +177,7 @@ async function Ae(e, t, n) {
|
|
|
177
177
|
return l = new xe(m).parse(e, ""), await Promise.all(p), t.signal.throwIfAborted(), { root: l, textures: d };
|
|
178
178
|
} catch (u) {
|
|
179
179
|
if (s.abort(u), await Promise.allSettled(p), l) {
|
|
180
|
-
const { disposeModelResources: w } = await import("./stage-
|
|
180
|
+
const { disposeModelResources: w } = await import("./stage-BPiGudYP.js").then((f) => f.r);
|
|
181
181
|
w(l, d);
|
|
182
182
|
} else for (const w of d) w.dispose();
|
|
183
183
|
throw u;
|