@nebula-spatial/viewer 0.3.0 → 0.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/CHANGELOG.md +93 -49
- package/README.md +593 -218
- package/dist/assets/types.d.ts +234 -0
- package/dist/cad/types.d.ts +16 -26
- package/dist/index-Lsk3kHNe.js +6718 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +4 -4924
- package/dist/mujoco-runtime-s85kwjo7.js +208 -0
- package/dist/plugins.d.ts +47 -3
- package/dist/session-B_UiRctl.js +762 -0
- package/dist/session-CGaj14lM.js +93 -0
- package/dist/stage-BxROW_dP.js +421 -0
- package/dist/types.d.ts +24 -2
- package/dist/ui/types.d.ts +45 -0
- package/dist/usd-session-Dcd20Z6G.js +522 -0
- package/package.json +7 -4
|
@@ -0,0 +1,522 @@
|
|
|
1
|
+
import { Group as ot, Mesh as at, Vector3 as _, Quaternion as j, Matrix4 as X, Box3 as ht } from "three";
|
|
2
|
+
import { USDLoader as It } from "@openusd-wasm/three-loader";
|
|
3
|
+
import { PXR as $t } from "@openusd-wasm/pxr";
|
|
4
|
+
import { c as Rt, a as xt, b as At, d as Nt, S as Mt } from "./session-B_UiRctl.js";
|
|
5
|
+
import { d as _t } from "./stage-BxROW_dP.js";
|
|
6
|
+
async function ee(t, e, n) {
|
|
7
|
+
var w;
|
|
8
|
+
Bt();
|
|
9
|
+
const s = n.hasLocalFiles(), d = t.entryPath ?? t.filename ?? "asset.usd", S = s ? d : await zt(t, d), l = s ? n.getBlob(d) : await Gt(t), r = d.split("/").pop() || "asset.usd", i = d.split("/").slice(0, -1).join("/"), y = e.options.openUsdBaseUrl ? { baseURL: e.options.openUsdBaseUrl } : void 0, p = t.hasResourceResolver && !s ? await new $t().load(y) : void 0;
|
|
10
|
+
p && await Ht(p, t, l, r, n);
|
|
11
|
+
const g = new It({
|
|
12
|
+
convertZUp: !1,
|
|
13
|
+
loadLights: !1,
|
|
14
|
+
preserveStage: !0,
|
|
15
|
+
...p ? { pxr: p } : y ? { pxrOptions: y } : {}
|
|
16
|
+
});
|
|
17
|
+
let c = null, I = null, u = null, D = null;
|
|
18
|
+
try {
|
|
19
|
+
if (c = await g.parseAsync(l, {
|
|
20
|
+
sourcePath: S,
|
|
21
|
+
fileName: r,
|
|
22
|
+
files: s || t.hasResourceResolver ? n.asSourceRecord(i) : void 0,
|
|
23
|
+
// A local package is the complete browser-visible file set. Resolving it
|
|
24
|
+
// through fetch would both hide missing uploads and accept SPA fallback
|
|
25
|
+
// responses as binary USD layers.
|
|
26
|
+
autoResolveAssets: !s && !t.hasResourceResolver,
|
|
27
|
+
// USD layer references are authoritative; disable directory guessing because SPA fallback pages may be returned as HTTP 200.
|
|
28
|
+
assetSearchRoots: [],
|
|
29
|
+
textureResolver: s || t.hasResourceResolver ? (a) => {
|
|
30
|
+
for (const $ of [a.resolvedPath, a.path, a.url])
|
|
31
|
+
if ($) {
|
|
32
|
+
const L = n.objectUrlIfPresent($, i);
|
|
33
|
+
if (L) return L;
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
} : void 0,
|
|
37
|
+
preserveStage: !0
|
|
38
|
+
}), e.signal.aborted) throw Ut();
|
|
39
|
+
const b = Yt(c);
|
|
40
|
+
c.scene.scale.multiplyScalar(b.metersPerUnit), c.data.stage.upAxis.toUpperCase() === "Y" && c.scene.quaternion.setFromAxisAngle(new _(1, 0, 0), Math.PI / 2), c.scene.updateMatrixWorld(!0);
|
|
41
|
+
const A = c.data.physics.rigidBodies.filter((a) => a.enabled), M = c.data.joints.filter((a) => a.enabled), O = At();
|
|
42
|
+
A.some((a) => a.startsAsleep === !0) && O.droppedFeatures.push({ code: "E_USD_STARTS_ASLEEP", message: "USD startsAsleep 无法在 MuJoCo 初始化阶段保证完全等价,已按正常初始状态加载。", source: "usd-physics" });
|
|
43
|
+
const R = Nt({
|
|
44
|
+
id: S,
|
|
45
|
+
name: S.split("/").pop() ?? S,
|
|
46
|
+
source: { format: "usd", uri: S },
|
|
47
|
+
links: A.map((a) => ({ id: a.path, name: a.name, visuals: [], collisions: [] })),
|
|
48
|
+
joints: M.flatMap((a) => a.body0 && a.body1 ? [{
|
|
49
|
+
id: a.path,
|
|
50
|
+
type: a.jointType,
|
|
51
|
+
parent: a.body0,
|
|
52
|
+
child: a.body1,
|
|
53
|
+
origin: { position: a.localPos1.map(($) => $ * b.metersPerUnit), rotationRpy: [0, 0, 0] },
|
|
54
|
+
axis: gt(a.axis).applyQuaternion(H(a.localRot1 ?? [1, 0, 0, 0])).toArray(),
|
|
55
|
+
...Number.isFinite(a.lowerLimit) && Number.isFinite(a.upperLimit) ? { limits: {
|
|
56
|
+
lower: a.lowerLimit * (a.jointType === "revolute" ? Math.PI / 180 : b.metersPerUnit),
|
|
57
|
+
upper: a.upperLimit * (a.jointType === "revolute" ? Math.PI / 180 : b.metersPerUnit)
|
|
58
|
+
} } : {}
|
|
59
|
+
}] : []),
|
|
60
|
+
diagnostics: O,
|
|
61
|
+
extension: { rigidBodyCount: A.length, jointCount: M.length }
|
|
62
|
+
});
|
|
63
|
+
if (A.length === 0) {
|
|
64
|
+
const a = yt(c), $ = c.scene, L = c;
|
|
65
|
+
return u = { root: $, sync() {
|
|
66
|
+
}, dispose() {
|
|
67
|
+
L.dispose();
|
|
68
|
+
} }, c = null, st(R, u, !1, () => {
|
|
69
|
+
}, void 0, void 0, a);
|
|
70
|
+
}
|
|
71
|
+
let P;
|
|
72
|
+
try {
|
|
73
|
+
P = Ct(c, A.map((a) => a.path), b), (w = P.massOverrides) != null && w.length && O.approximations.push({ code: "USD_INFERRED_INERTIA", message: "保留显式总质量;惯量按碰撞几何的密度分布推导并同比缩放,重叠体积会重复计入。", source: "usd-physics" });
|
|
74
|
+
} catch (a) {
|
|
75
|
+
if (!vt(a)) throw a;
|
|
76
|
+
const $ = Tt(a);
|
|
77
|
+
O.droppedFeatures.push($);
|
|
78
|
+
const L = yt(c), G = c.scene, f = c;
|
|
79
|
+
return u = { root: G, sync() {
|
|
80
|
+
}, dispose() {
|
|
81
|
+
f.dispose();
|
|
82
|
+
} }, c = null, st(R, u, !1, () => {
|
|
83
|
+
}, void 0, $, L);
|
|
84
|
+
}
|
|
85
|
+
const x = D = Lt(c, A.map((a) => a.path)), { MujocoRuntime: K } = await import("./mujoco-runtime-s85kwjo7.js");
|
|
86
|
+
if (I = new K(), await I.initialize(P, e.options.mujocoBaseUrl), e.signal.aborted) throw Ut();
|
|
87
|
+
u = new Mt(I, x), D = null;
|
|
88
|
+
const o = c, E = I, N = u;
|
|
89
|
+
return c = null, I = null, u = null, st(R, N, !0, () => {
|
|
90
|
+
E.dispose(), o.dispose();
|
|
91
|
+
}, E);
|
|
92
|
+
} catch (b) {
|
|
93
|
+
throw D && _t(D.root), u == null || u.dispose(), I == null || I.dispose(), c == null || c.dispose(), b;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function st(t, e, n, s, d, S, l) {
|
|
97
|
+
let r = !1;
|
|
98
|
+
return {
|
|
99
|
+
asset: t,
|
|
100
|
+
binding: e,
|
|
101
|
+
hasPhysics: n,
|
|
102
|
+
runtime: d,
|
|
103
|
+
fallback: S,
|
|
104
|
+
collision: l,
|
|
105
|
+
step(i) {
|
|
106
|
+
if (!d) return !1;
|
|
107
|
+
const y = d.step(i);
|
|
108
|
+
return y && e.sync(), y;
|
|
109
|
+
},
|
|
110
|
+
reset() {
|
|
111
|
+
d == null || d.reset(), e.sync();
|
|
112
|
+
},
|
|
113
|
+
dispose() {
|
|
114
|
+
r || (r = !0, l == null || l.dispose(), e.dispose(), s());
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
const Ot = /* @__PURE__ */ new Set([
|
|
119
|
+
"E_USD_PHYSICS_UNSUPPORTED",
|
|
120
|
+
"E_USD_UNBAKED_BODY_SCALE",
|
|
121
|
+
"E_USD_INVALID_JOINT_REFERENCE",
|
|
122
|
+
"E_USD_UNSUPPORTED_JOINT",
|
|
123
|
+
"E_USD_MULTIPLE_PARENTS",
|
|
124
|
+
"E_USD_JOINT_CYCLE",
|
|
125
|
+
"E_USD_UNSUPPORTED_DRIVE",
|
|
126
|
+
"E_USD_INVALID_MASS",
|
|
127
|
+
"E_USD_PARTIAL_INERTIA",
|
|
128
|
+
"E_USD_MISSING_COLLISION_GEOMETRY",
|
|
129
|
+
"E_USD_UNREACHABLE_BODIES",
|
|
130
|
+
"E_USD_MULTIPLE_PHYSICS_SCENES",
|
|
131
|
+
"E_USD_NONUNIFORM_RADIAL_SCALE",
|
|
132
|
+
"E_USD_UNSUPPORTED_GEOMETRY",
|
|
133
|
+
"E_USD_CONSTRAINED_INITIAL_VELOCITY",
|
|
134
|
+
"E_USD_UNSUPPORTED_APPROXIMATION"
|
|
135
|
+
]);
|
|
136
|
+
function vt(t) {
|
|
137
|
+
const e = Et(t);
|
|
138
|
+
return e !== null && Ot.has(e);
|
|
139
|
+
}
|
|
140
|
+
function Tt(t) {
|
|
141
|
+
const e = Et(t) ?? "E_USD_PHYSICS_UNSUPPORTED", n = t instanceof Error ? t.message : String(t);
|
|
142
|
+
return {
|
|
143
|
+
code: e,
|
|
144
|
+
message: `USD 视觉场景已加载,但物理语义无法可靠映射,已降级为仅视觉预览。${n ? ` ${n}` : ""}`,
|
|
145
|
+
source: "usd-physics"
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
function Et(t) {
|
|
149
|
+
if (!t || typeof t != "object" || !("code" in t)) return null;
|
|
150
|
+
const e = t.code;
|
|
151
|
+
return typeof e == "string" ? e : null;
|
|
152
|
+
}
|
|
153
|
+
function Lt(t, e) {
|
|
154
|
+
t.scene.updateMatrixWorld(!0);
|
|
155
|
+
const n = new ot();
|
|
156
|
+
n.name = "USD simulation";
|
|
157
|
+
const s = /* @__PURE__ */ new Map(), d = /* @__PURE__ */ new Map();
|
|
158
|
+
for (const l of e) {
|
|
159
|
+
const r = t.index.objectsByPath.get(l);
|
|
160
|
+
if (!r) throw h(`USD rigid body node is missing: ${l}`, "E_USD_PHYSICS_UNSUPPORTED");
|
|
161
|
+
const i = new ot();
|
|
162
|
+
i.name = l, n.add(i), s.set(l, i);
|
|
163
|
+
const y = new _(), p = new j();
|
|
164
|
+
r.matrixWorld.decompose(y, p, new _()), d.set(l, new X().compose(y, p, new _(1, 1, 1)));
|
|
165
|
+
}
|
|
166
|
+
const S = [];
|
|
167
|
+
t.scene.traverse((l) => {
|
|
168
|
+
var p;
|
|
169
|
+
if (!(l instanceof at)) return;
|
|
170
|
+
let r = l, i, y = !0;
|
|
171
|
+
for (; r; ) {
|
|
172
|
+
y && (y = r.visible);
|
|
173
|
+
const g = (p = r.userData.usd) == null ? void 0 : p.path;
|
|
174
|
+
!i && g && s.has(g) && (i = g), r = r.parent;
|
|
175
|
+
}
|
|
176
|
+
y && S.push([l, i]);
|
|
177
|
+
});
|
|
178
|
+
for (const [l, r] of S) {
|
|
179
|
+
const i = l.clone(!1);
|
|
180
|
+
i.geometry = l.geometry.clone(), i.material = Array.isArray(l.material) ? l.material.map((y) => y.clone()) : l.material.clone(), i.matrix.copy(r ? d.get(r) : new X()).invert().multiply(l.matrixWorld), i.matrix.decompose(i.position, i.quaternion, i.scale), (r ? s.get(r) : n).add(i);
|
|
181
|
+
}
|
|
182
|
+
return { root: n, bindings: s, expectedLinkCount: s.size };
|
|
183
|
+
}
|
|
184
|
+
function yt(t) {
|
|
185
|
+
t.scene.updateMatrixWorld(!0);
|
|
186
|
+
const e = new ot();
|
|
187
|
+
e.name = "USD authored collisions";
|
|
188
|
+
const n = Rt(), s = /* @__PURE__ */ new Set();
|
|
189
|
+
for (const S of t.data.physics.colliders) {
|
|
190
|
+
if (!S.enabled) continue;
|
|
191
|
+
const l = t.index.objectsByPath.get(S.path);
|
|
192
|
+
l && l.traverse((r) => {
|
|
193
|
+
if (!(r instanceof at) || s.has(r)) return;
|
|
194
|
+
s.add(r);
|
|
195
|
+
const i = xt(r.geometry, n);
|
|
196
|
+
i.name = `Collision:${S.path}`, i.matrixAutoUpdate = !1, i.matrix.copy(r.matrixWorld), i.renderOrder = 9, i.userData.usdColliderPath = S.path, e.add(i);
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
if (e.children.length === 0) {
|
|
200
|
+
n.dispose();
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
e.visible = !1;
|
|
204
|
+
let d = !1;
|
|
205
|
+
return {
|
|
206
|
+
root: e,
|
|
207
|
+
sync() {
|
|
208
|
+
},
|
|
209
|
+
dispose() {
|
|
210
|
+
d || (d = !0, e.removeFromParent(), _t(e));
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
function Ct(t, e, n) {
|
|
215
|
+
t.scene.updateMatrixWorld(!0);
|
|
216
|
+
const s = [], d = [], S = [], l = [], r = [], i = [], y = [], { metersPerUnit: p, kilogramsPerUnit: g } = n, c = new Map(e.map((o) => {
|
|
217
|
+
const E = t.index.rigidBodiesByPath.get(o);
|
|
218
|
+
if (!E) throw h(`USD rigid body metadata is missing: ${o}`, "E_USD_PHYSICS_UNSUPPORTED");
|
|
219
|
+
return [o, E];
|
|
220
|
+
})), I = /* @__PURE__ */ new Map();
|
|
221
|
+
for (const o of c.keys()) {
|
|
222
|
+
const E = t.index.objectsByPath.get(o);
|
|
223
|
+
if (!E) throw h(`USD rigid body node is missing: ${o}`, "E_USD_PHYSICS_UNSUPPORTED");
|
|
224
|
+
const N = new _(), a = new j(), $ = new _();
|
|
225
|
+
if (E.matrixWorld.decompose(N, a, $), $.clone().divideScalar(p).toArray().some((G) => Math.abs(Math.abs(G) - 1) > 1e-5))
|
|
226
|
+
throw h(`USD rigid body has unbaked scale: ${o}`, "E_USD_UNBAKED_BODY_SCALE");
|
|
227
|
+
I.set(o, new X().compose(N, a, new _(1, 1, 1)));
|
|
228
|
+
}
|
|
229
|
+
const u = /* @__PURE__ */ new Map();
|
|
230
|
+
for (const o of t.data.joints.filter((E) => E.enabled)) {
|
|
231
|
+
if (!o.body1 || !c.has(o.body1) || o.body0 && !c.has(o.body0))
|
|
232
|
+
throw h(`USD joint references an unknown rigid body: ${o.path}`, "E_USD_INVALID_JOINT_REFERENCE");
|
|
233
|
+
if (!["fixed", "revolute", "prismatic"].includes(o.jointType))
|
|
234
|
+
throw h(`Unsupported USD joint type ${o.jointType}: ${o.path}`, "E_USD_UNSUPPORTED_JOINT");
|
|
235
|
+
if (u.has(o.body1)) throw h(`USD rigid body has multiple parent joints: ${o.body1}`, "E_USD_MULTIPLE_PARENTS");
|
|
236
|
+
u.set(o.body1, o), o.body0 && o.collisionEnabled !== !0 && S.push(`<exclude body1="${V(o.body0)}" body2="${V(o.body1)}"/>`);
|
|
237
|
+
}
|
|
238
|
+
let D = 0;
|
|
239
|
+
const w = /* @__PURE__ */ new Set(), b = (o, E = new X()) => {
|
|
240
|
+
var ct, lt, ut, dt, pt;
|
|
241
|
+
if (w.has(o)) throw h(`USD joint graph contains a cycle at ${o}`, "E_USD_JOINT_CYCLE");
|
|
242
|
+
w.add(o);
|
|
243
|
+
const N = c.get(o), a = I.get(o), $ = E.clone().invert().multiply(a), L = new _(), G = new j();
|
|
244
|
+
$.decompose(L, G, new _());
|
|
245
|
+
const f = u.get(o), z = [`<body name="${V(o)}" pos="${T(L.toArray())}" quat="${k(G)}">`];
|
|
246
|
+
if (!f && !N.kinematicEnabled && z.push("<freejoint/>"), f && [...N.velocity ?? [], ...N.angularVelocity ?? []].some((U) => U !== 0))
|
|
247
|
+
throw h(`Constrained USD initial velocity cannot be projected reliably: ${o}`, "E_USD_CONSTRAINED_INITIAL_VELOCITY");
|
|
248
|
+
if (!f && !N.kinematicEnabled) {
|
|
249
|
+
const U = new _(...N.velocity ?? [0, 0, 0]).multiplyScalar(p).applyQuaternion(t.scene.quaternion), v = new _(...N.angularVelocity ?? [0, 0, 0]).multiplyScalar(Math.PI / 180).applyQuaternion(t.scene.quaternion).applyQuaternion(new j().setFromRotationMatrix(a).invert());
|
|
250
|
+
y.push({ body: o, velocity: [...U.toArray(), ...v.toArray()] });
|
|
251
|
+
}
|
|
252
|
+
if (f && f.jointType !== "fixed") {
|
|
253
|
+
const U = f.jointType === "revolute" ? "hinge" : "slide", v = gt(f.axis).applyQuaternion(H(f.localRot1 ?? [1, 0, 0, 0])), C = U === "hinge" ? Math.PI / 180 : p, tt = new j().setFromRotationMatrix(E), W = new j().setFromRotationMatrix(a), q = tt.clone().multiply(H(f.localRot0 ?? [1, 0, 0, 0])).invert().multiply(W).multiply(H(f.localRot1 ?? [1, 0, 0, 0]));
|
|
254
|
+
let Y = 0;
|
|
255
|
+
if (U === "hinge") {
|
|
256
|
+
const B = f.axis === "Y" ? q.y : f.axis === "Z" ? q.z : q.x;
|
|
257
|
+
Y = 2 * Math.atan2(B, q.w), Y = Math.atan2(Math.sin(Y), Math.cos(Y));
|
|
258
|
+
} else {
|
|
259
|
+
const B = new _(...f.localPos0).multiplyScalar(p).applyMatrix4(E);
|
|
260
|
+
Y = new _(...f.localPos1).multiplyScalar(p).applyMatrix4(a).sub(B).dot(v.clone().applyQuaternion(W));
|
|
261
|
+
}
|
|
262
|
+
const ft = Number.isFinite(f.lowerLimit) && Number.isFinite(f.upperLimit) && Math.abs(f.lowerLimit) < 1e20 && Math.abs(f.upperLimit) < 1e20, wt = ft && f.lowerLimit < f.upperLimit, Q = g * (U === "slide" ? p : p * p), et = Z(t, f.path, `physxJointAxis:${U === "slide" ? "linear" : "angular"}:dynamicFrictionEffort`), Pt = typeof et == "number" && Number.isFinite(et) ? et * Q : 0;
|
|
263
|
+
z.push(`<joint name="${V(f.path)}" type="${U}" pos="${T(f.localPos1.map((B) => B * p))}" axis="${T(v.toArray())}" ref="${Y}" ${wt ? `range="${f.lowerLimit * C} ${f.upperLimit * C}"` : 'limited="false"'} frictionloss="${Pt}" armature="0.00001"/>`), ft && f.lowerLimit === f.upperLimit && d.push(`<joint joint1="${V(f.path)}" polycoef="${f.lowerLimit * C - Y} 0 0 0 0"/>`);
|
|
264
|
+
const F = f.drive;
|
|
265
|
+
if (F && ((F.stiffness ?? 0) !== 0 || (F.damping ?? 0) !== 0)) {
|
|
266
|
+
if (F.type === "acceleration") throw h(`Unsupported USD acceleration drive: ${f.path}`, "E_USD_UNSUPPORTED_DRIVE");
|
|
267
|
+
const B = (F.stiffness ?? 0) * Q / C, nt = (F.damping ?? 0) * Q / C, J = F.maxForce == null ? null : F.maxForce * Q;
|
|
268
|
+
r.push(`<general joint="${V(f.path)}" gainprm="1" biastype="affine" biasprm="0 ${-B} ${-nt}" ${J != null && Number.isFinite(J) ? `forcelimited="true" forcerange="${-J} ${J}"` : ""}/>`), i.push(B * (F.targetPosition ?? 0) * C + nt * (F.targetVelocity ?? 0) * C);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
const m = N.mass;
|
|
272
|
+
if ((m == null ? void 0 : m.mass) != null && (!Number.isFinite(m.mass) || m.mass < 0)) throw h(`USD rigid body mass must be finite and non-negative: ${o}`, "E_USD_INVALID_MASS");
|
|
273
|
+
if ((m == null ? void 0 : m.mass) != null && m.mass > 0 && ((ct = m.centerOfMass) != null && ct.every(Number.isFinite)) && ((lt = m.diagonalInertia) != null && lt.every((U) => Number.isFinite(U) && U > 0)) && ((ut = m.principalAxes) != null && ut.every(Number.isFinite)))
|
|
274
|
+
z.push(`<inertial mass="${m.mass * g}" pos="${T(m.centerOfMass.map((U) => U * p))}" quat="${k(H(m.principalAxes))}" diaginertia="${T(m.diagonalInertia.map((U) => U * g * p * p))}"/>`);
|
|
275
|
+
else {
|
|
276
|
+
if ((dt = m == null ? void 0 : m.diagonalInertia) != null && dt.some((U) => U > 0))
|
|
277
|
+
throw h(`USD rigid body has incomplete explicit inertia: ${o}`, "E_USD_PARTIAL_INERTIA");
|
|
278
|
+
if ((m == null ? void 0 : m.mass) != null && m.mass > 0) {
|
|
279
|
+
if ((pt = m.centerOfMass) != null && pt.every(Number.isFinite) && m.centerOfMass.some((U) => U !== 0)) throw h(`Custom center of mass requires explicit inertia: ${o}`, "E_USD_PARTIAL_INERTIA");
|
|
280
|
+
l.push({ body: o, mass: m.mass * g });
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
const rt = t.data.physics.colliders.filter((U) => U.enabled && U.bodyPath === o);
|
|
284
|
+
if (!rt.length) throw h(`USD rigid body has no authored collision geometry: ${o}`, "E_USD_MISSING_COLLISION_GEOMETRY");
|
|
285
|
+
for (const U of rt) {
|
|
286
|
+
const v = t.data.physics.materials.find((Y) => Y.path === U.material), C = (m == null ? void 0 : m.density) != null && m.density > 0 ? m.density : v == null ? void 0 : v.density, tt = C != null && C > 0 ? C * g / p ** 3 : 1e3, W = (v == null ? void 0 : v.dynamicFriction) ?? 0.4, q = `density="${tt}" group="3" contype="1" conaffinity="1" friction="${W} 0.01 0.0002"`;
|
|
287
|
+
z.push(mt(t, U, o, a, D++, s, q));
|
|
288
|
+
}
|
|
289
|
+
for (const [U, v] of u) v.body0 === o && z.push(b(U, a));
|
|
290
|
+
return z.push("</body>"), z.join("");
|
|
291
|
+
}, A = t.data.physics.colliders.filter((o) => o.enabled && !o.bodyPath), M = A.map((o) => {
|
|
292
|
+
const E = t.data.physics.materials.find((L) => L.path === o.material), N = (E == null ? void 0 : E.density) != null && E.density > 0 ? E.density * g / p ** 3 : 1e3, a = (E == null ? void 0 : E.dynamicFriction) ?? 0.4, $ = `density="${N}" group="3" contype="1" conaffinity="1" friction="${a} 0.01 0.0002"`;
|
|
293
|
+
return mt(t, o, "", new X(), D++, s, $);
|
|
294
|
+
}).join(""), O = Ft(t, e, A) ? `<geom name="__viewer_default_ground__" type="plane" pos="0 0 ${jt(t, e)}" size="1 1 0.1" group="3" contype="1" conaffinity="1" friction="0.8 0.01 0.0002"/>` : "", P = [...c.keys()].filter((o) => {
|
|
295
|
+
var E;
|
|
296
|
+
return !((E = u.get(o)) != null && E.body0);
|
|
297
|
+
}).map((o) => b(o)).join("");
|
|
298
|
+
if (w.size !== c.size) throw h("USD joint graph contains unreachable bodies or a cycle.", "E_USD_UNREACHABLE_BODIES");
|
|
299
|
+
if (t.data.physics.scenes.length > 1) throw h("Multiple USD PhysicsScene prims are not supported.", "E_USD_MULTIPLE_PHYSICS_SCENES");
|
|
300
|
+
const x = t.data.physics.scenes[0], K = x != null && x.gravityDirection && x.gravityMagnitude != null && x.gravityMagnitude >= 0 ? new _(...x.gravityDirection).normalize().multiplyScalar(x.gravityMagnitude * p).applyQuaternion(t.scene.quaternion) : new _(0, 0, -9.81);
|
|
301
|
+
return {
|
|
302
|
+
format: "mjcf",
|
|
303
|
+
files: /* @__PURE__ */ new Map(),
|
|
304
|
+
initialControls: i,
|
|
305
|
+
initialVelocities: y,
|
|
306
|
+
massOverrides: l,
|
|
307
|
+
xml: `<mujoco><compiler angle="radian" autolimits="true"/><option integrator="implicitfast" gravity="${T(K.toArray())}"><flag filterparent="disable"/></option><default><geom margin="0.0015" solref="0.004 1"/></default>${s.length ? `<asset>${s.join("")}</asset>` : ""}<worldbody>${O}${M}${P}</worldbody>${S.length ? `<contact>${S.join("")}</contact>` : ""}${d.length ? `<equality>${d.join("")}</equality>` : ""}${r.length ? `<actuator>${r.join("")}</actuator>` : ""}</mujoco>`
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
function Ft(t, e, n = t.data.physics.colliders.filter((s) => s.enabled && !s.bodyPath)) {
|
|
311
|
+
if (!e.length || n.some((r) => r.typeName.toLowerCase().includes("plane"))) return !1;
|
|
312
|
+
const s = it(t, t.data.physics.colliders.filter(
|
|
313
|
+
(r) => r.enabled && r.bodyPath && e.includes(r.bodyPath)
|
|
314
|
+
));
|
|
315
|
+
if (s.isEmpty()) return n.length === 0;
|
|
316
|
+
const d = Math.max(s.max.x - s.min.x, 1e-6), S = Math.max(s.max.y - s.min.y, 1e-6), l = Math.max(d, S, s.max.z - s.min.z) * 0.05;
|
|
317
|
+
return !n.some((r) => {
|
|
318
|
+
const i = it(t, [r]);
|
|
319
|
+
if (i.isEmpty()) return !1;
|
|
320
|
+
const y = i.max.x >= s.min.x && i.min.x <= s.max.x, p = i.max.y >= s.min.y && i.min.y <= s.max.y;
|
|
321
|
+
return y && p && i.max.z <= s.min.z + l;
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
function jt(t, e) {
|
|
325
|
+
const n = it(t, t.data.physics.colliders.filter(
|
|
326
|
+
(d) => d.enabled && d.bodyPath && e.includes(d.bodyPath)
|
|
327
|
+
)), s = n.isEmpty() ? 0 : n.min.z;
|
|
328
|
+
return Number.isFinite(s) ? String(s) : "0";
|
|
329
|
+
}
|
|
330
|
+
function it(t, e) {
|
|
331
|
+
const n = new ht();
|
|
332
|
+
for (const s of e) {
|
|
333
|
+
const d = t.index.objectsByPath.get(s.path);
|
|
334
|
+
d && n.union(new ht().setFromObject(d));
|
|
335
|
+
}
|
|
336
|
+
return n;
|
|
337
|
+
}
|
|
338
|
+
function gt(t) {
|
|
339
|
+
return t === "Y" ? new _(0, 1, 0) : t === "Z" ? new _(0, 0, 1) : new _(1, 0, 0);
|
|
340
|
+
}
|
|
341
|
+
function mt(t, e, n, s, d, S, l) {
|
|
342
|
+
const r = t.index.objectsByPath.get(e.path);
|
|
343
|
+
if (!r) throw h(`USD collider node is missing: ${e.path}`, "E_USD_MISSING_COLLISION_GEOMETRY");
|
|
344
|
+
const i = s.clone().invert().multiply(r.matrixWorld), y = new _(), p = new j(), g = new _();
|
|
345
|
+
i.decompose(y, p, g);
|
|
346
|
+
const c = e.typeName.replace(/^UsdGeom/, "").toLowerCase(), I = (u, D) => {
|
|
347
|
+
const w = Z(t, e.path, u);
|
|
348
|
+
return typeof w == "number" ? w : D;
|
|
349
|
+
};
|
|
350
|
+
if (c.includes("cube")) {
|
|
351
|
+
const u = Math.abs(I("size", 2)) / 2;
|
|
352
|
+
return `<geom type="box" pos="${T(y.toArray())}" quat="${k(p)}" size="${T([Math.abs(g.x) * u, Math.abs(g.y) * u, Math.abs(g.z) * u])}" ${l}/>`;
|
|
353
|
+
}
|
|
354
|
+
if (c.includes("plane")) {
|
|
355
|
+
if (n) throw h(`USD plane collider requires a static world owner: ${e.path}`, "E_USD_UNSUPPORTED_GEOMETRY");
|
|
356
|
+
const u = String(Z(t, e.path, "axis") ?? "Z").toUpperCase(), w = { X: new _(1, 0, 0), Y: new _(0, 1, 0), Z: new _(0, 0, 1) }[u];
|
|
357
|
+
if (!w) throw h(`Unsupported USD plane axis: ${u}`, "E_USD_UNSUPPORTED_GEOMETRY");
|
|
358
|
+
const b = p.clone().multiply(new j().setFromUnitVectors(new _(0, 0, 1), w));
|
|
359
|
+
return `<geom type="plane" pos="${T(y.toArray())}" quat="${k(b)}" size="1 1 0.1" ${l}/>`;
|
|
360
|
+
}
|
|
361
|
+
if (c.includes("cone"))
|
|
362
|
+
throw h(`MuJoCo has no analytic cone geom; a validated mesh conversion is required: ${e.path}`, "E_USD_UNSUPPORTED_GEOMETRY");
|
|
363
|
+
if (c.includes("sphere")) {
|
|
364
|
+
const u = [Math.abs(g.x), Math.abs(g.y), Math.abs(g.z)];
|
|
365
|
+
if (Math.max(...u) - Math.min(...u) > 1e-6) throw h(`USD sphere collider does not support non-uniform scale: ${e.path}`, "E_USD_NONUNIFORM_RADIAL_SCALE");
|
|
366
|
+
return `<geom type="sphere" pos="${T(y.toArray())}" quat="${k(p)}" size="${u[0] * I("radius", 1)}" ${l}/>`;
|
|
367
|
+
}
|
|
368
|
+
if (c.includes("cylinder") || c.includes("capsule")) {
|
|
369
|
+
const u = String(Z(t, e.path, "axis") ?? "Z").toUpperCase(), D = { X: 0, Y: 1, Z: 2 }[u];
|
|
370
|
+
if (D === void 0) throw h(`Unsupported USD collider axis: ${u}`, "E_USD_UNSUPPORTED_GEOMETRY");
|
|
371
|
+
const w = [Math.abs(g.x), Math.abs(g.y), Math.abs(g.z)], b = w.filter((P, x) => x !== D);
|
|
372
|
+
if (Math.abs(b[0] - b[1]) > 1e-6) throw h(`USD ${e.typeName} collider does not support non-uniform radial scale: ${e.path}`, "E_USD_NONUNIFORM_RADIAL_SCALE");
|
|
373
|
+
const A = new _(...{ X: [1, 0, 0], Y: [0, 1, 0], Z: [0, 0, 1] }[u]), M = p.clone().multiply(new j().setFromUnitVectors(new _(0, 0, 1), A)), O = I("radius", 1) * b[0], R = I("height", 2) * w[D] / 2;
|
|
374
|
+
return `<geom type="${c.includes("capsule") ? "capsule" : "cylinder"}" pos="${T(y.toArray())}" quat="${k(M)}" size="${O} ${R}" ${l}/>`;
|
|
375
|
+
}
|
|
376
|
+
if (c.includes("mesh")) {
|
|
377
|
+
if (e.approximation && !["none", "convexHull"].includes(e.approximation)) throw h(`USD mesh approximation cannot be mapped exactly: ${e.path}`, "E_USD_UNSUPPORTED_APPROXIMATION");
|
|
378
|
+
const u = [];
|
|
379
|
+
if (r.traverse((M) => {
|
|
380
|
+
M instanceof at && u.push(M);
|
|
381
|
+
}), !u.length) throw h(`USD collider has no mesh geometry: ${e.path}`, "E_USD_MISSING_COLLISION_GEOMETRY");
|
|
382
|
+
const D = [], w = [];
|
|
383
|
+
let b = 0;
|
|
384
|
+
for (const M of u) {
|
|
385
|
+
const O = M.geometry.getAttribute("position");
|
|
386
|
+
for (let P = 0; P < O.count; P++) {
|
|
387
|
+
const x = new _().fromBufferAttribute(O, P).applyMatrix4(s.clone().invert().multiply(M.matrixWorld));
|
|
388
|
+
D.push(x.x, x.y, x.z);
|
|
389
|
+
}
|
|
390
|
+
const R = M.geometry.index;
|
|
391
|
+
for (let P = 0; P < (R ? R.count : O.count); P += 3) w.push(b + (R ? R.getX(P) : P), b + (R ? R.getX(P + 1) : P + 1), b + (R ? R.getX(P + 2) : P + 2));
|
|
392
|
+
b += O.count;
|
|
393
|
+
}
|
|
394
|
+
const A = `collision_${d}`;
|
|
395
|
+
return S.push(`<mesh name="${A}" vertex="${T(D)}" face="${T(w)}"/>`), `<geom type="mesh" mesh="${A}" ${l}/>`;
|
|
396
|
+
}
|
|
397
|
+
throw h(`Unsupported USD collider geometry: ${e.typeName}`, "E_USD_UNSUPPORTED_GEOMETRY");
|
|
398
|
+
}
|
|
399
|
+
function Yt(t) {
|
|
400
|
+
const e = t.stage.GetPseudoRoot();
|
|
401
|
+
try {
|
|
402
|
+
const n = Number(e.GetMetadata("metersPerUnit") ?? 0.01), s = Number(e.GetMetadata("kilogramsPerUnit") ?? 1);
|
|
403
|
+
if (!Number.isFinite(n) || n <= 0) throw h("USD metersPerUnit must be finite and positive.", "E_USD_INVALID_UNITS");
|
|
404
|
+
if (!Number.isFinite(s) || s <= 0) throw h("USD kilogramsPerUnit must be finite and positive.", "E_USD_INVALID_UNITS");
|
|
405
|
+
return { metersPerUnit: n, kilogramsPerUnit: s };
|
|
406
|
+
} finally {
|
|
407
|
+
e.delete();
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
function H(t) {
|
|
411
|
+
const [e = 1, n = 0, s = 0, d = 0] = t;
|
|
412
|
+
return new j(n, s, d, e).normalize();
|
|
413
|
+
}
|
|
414
|
+
function T(t) {
|
|
415
|
+
return t.join(" ");
|
|
416
|
+
}
|
|
417
|
+
function k(t) {
|
|
418
|
+
return `${t.w} ${t.x} ${t.y} ${t.z}`;
|
|
419
|
+
}
|
|
420
|
+
function Z(t, e, n) {
|
|
421
|
+
var i, y;
|
|
422
|
+
const s = t.stage;
|
|
423
|
+
if (!s || !((y = (i = t.pxr) == null ? void 0 : i.Sdf) != null && y.Path)) return null;
|
|
424
|
+
const d = new t.pxr.Sdf.Path(e), S = s.GetPrimAtPath(d), l = S.GetAttribute(n), r = t.pxr.Usd.TimeCode.Default();
|
|
425
|
+
try {
|
|
426
|
+
return l.IsValid() ? l.Get(r) : null;
|
|
427
|
+
} finally {
|
|
428
|
+
r.delete(), l.delete(), S.delete(), d.delete();
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
function Bt() {
|
|
432
|
+
if (!globalThis.isSecureContext) throw h("OpenUSD requires a secure context.", "E_USD_SECURE_CONTEXT_REQUIRED");
|
|
433
|
+
if (!globalThis.crossOriginIsolated) throw h("OpenUSD requires COOP/COEP cross-origin isolation.", "E_USD_CROSS_ORIGIN_ISOLATION_REQUIRED");
|
|
434
|
+
}
|
|
435
|
+
async function zt(t, e) {
|
|
436
|
+
var n;
|
|
437
|
+
if (t.url)
|
|
438
|
+
try {
|
|
439
|
+
return new URL(t.url, ((n = globalThis.location) == null ? void 0 : n.href) ?? t.baseUrl ?? void 0).href;
|
|
440
|
+
} catch {
|
|
441
|
+
return t.url;
|
|
442
|
+
}
|
|
443
|
+
return t.resolve(e);
|
|
444
|
+
}
|
|
445
|
+
async function Gt(t) {
|
|
446
|
+
const e = t.url ?? await t.resolve(t.entryPath ?? ""), n = await fetch(e, { signal: t.signal });
|
|
447
|
+
if (!n.ok) throw h(`USD request failed (${n.status}): ${e}`, n.status === 401 || n.status === 403 ? "E_ASSET_AUTH" : "E_ASSET_FETCH");
|
|
448
|
+
return n.blob();
|
|
449
|
+
}
|
|
450
|
+
const qt = /* @__PURE__ */ new Set(["usd", "usda", "usdc"]), St = 1024, Vt = 8;
|
|
451
|
+
let kt = 0;
|
|
452
|
+
async function Ht(t, e, n, s, d) {
|
|
453
|
+
const S = `/tmp/viewer-usd-resolver/${++kt}`;
|
|
454
|
+
t.FS.createDir(S);
|
|
455
|
+
const l = [{ path: bt(s), blob: n, depth: 0 }], r = /* @__PURE__ */ new Set();
|
|
456
|
+
try {
|
|
457
|
+
for (; l.length > 0; ) {
|
|
458
|
+
e.signal.throwIfAborted();
|
|
459
|
+
const i = l.shift();
|
|
460
|
+
if (r.has(i.path) || i.depth > Vt) continue;
|
|
461
|
+
if (r.size >= St) throw h(`USD dependency limit exceeded (${St}).`, "E_ASSET_RESOURCE_LIMIT");
|
|
462
|
+
r.add(i.path);
|
|
463
|
+
const y = `${S}/${i.path}`;
|
|
464
|
+
t.FS.createDir(y.split("/").slice(0, -1).join("/")), t.FS.writeFile(y, new Uint8Array(await i.blob.arrayBuffer()));
|
|
465
|
+
const p = t.UsdUtils.ExtractSanitizedExternalReferences(y);
|
|
466
|
+
for (const g of p) {
|
|
467
|
+
const c = Xt(i.path, g);
|
|
468
|
+
if (!c || r.has(c)) continue;
|
|
469
|
+
const I = await e.resolve(c), u = await fetch(I, { signal: e.signal });
|
|
470
|
+
if (!u.ok) {
|
|
471
|
+
const w = u.status === 401 || u.status === 403 ? "E_ASSET_AUTH" : "E_ASSET_FETCH";
|
|
472
|
+
throw h(`USD dependency request failed (${u.status}): ${c}`, w);
|
|
473
|
+
}
|
|
474
|
+
const D = await u.blob();
|
|
475
|
+
d.put(c, D), qt.has(Wt(c)) && l.push({ path: c, blob: D, depth: i.depth + 1 });
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
} finally {
|
|
479
|
+
Dt(t, S);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
function Xt(t, e) {
|
|
483
|
+
if (!e || /^(?:data|blob):/i.test(e)) return null;
|
|
484
|
+
if (/^[a-z][a-z0-9+.-]*:/i.test(e) || e.startsWith("/")) return e;
|
|
485
|
+
const n = t.split("/").slice(0, -1).join("/");
|
|
486
|
+
return bt(n ? `${n}/${e}` : e);
|
|
487
|
+
}
|
|
488
|
+
function bt(t) {
|
|
489
|
+
const e = [];
|
|
490
|
+
for (const n of t.replaceAll("\\", "/").split("/"))
|
|
491
|
+
!n || n === "." || (n === ".." ? e.pop() : e.push(n));
|
|
492
|
+
return e.join("/");
|
|
493
|
+
}
|
|
494
|
+
function Wt(t) {
|
|
495
|
+
var e, n;
|
|
496
|
+
return ((n = (e = t.split(/[?#]/, 1)[0]) == null ? void 0 : e.split(".").pop()) == null ? void 0 : n.toLowerCase()) ?? "";
|
|
497
|
+
}
|
|
498
|
+
function Dt(t, e) {
|
|
499
|
+
if (t.FS.exists(e)) {
|
|
500
|
+
for (const n of t.FS.listDir(e))
|
|
501
|
+
n.isDir ? Dt(t, n.path) : t.FS.deleteFile(n.path);
|
|
502
|
+
t.FS.deleteDir(e);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
function V(t) {
|
|
506
|
+
return t.replaceAll("&", "&").replaceAll('"', """).replaceAll("<", "<").replaceAll(">", ">");
|
|
507
|
+
}
|
|
508
|
+
function h(t, e) {
|
|
509
|
+
return Object.assign(new Error(t), { code: e });
|
|
510
|
+
}
|
|
511
|
+
function Ut() {
|
|
512
|
+
return Object.assign(new Error("USD load aborted."), { name: "AbortError", code: "E_ABORTED" });
|
|
513
|
+
}
|
|
514
|
+
export {
|
|
515
|
+
yt as compileUsdCollisionOverlay,
|
|
516
|
+
Ct as compileUsdPhysics,
|
|
517
|
+
Lt as compileUsdVisual,
|
|
518
|
+
vt as isUsdPhysicsFallbackEligible,
|
|
519
|
+
ee as loadUsdSimulation,
|
|
520
|
+
Ht as resolveUsdResourcePackage,
|
|
521
|
+
Ft as shouldInjectUsdDefaultGround
|
|
522
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nebula-spatial/viewer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Read-only Three.js viewer facade",
|
|
5
5
|
"license": "ISC",
|
|
6
6
|
"engines": {
|
|
@@ -36,18 +36,21 @@
|
|
|
36
36
|
"prepack": "npm run build"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@
|
|
39
|
+
"@mujoco/mujoco": "^3.12.0",
|
|
40
|
+
"@nebula-spatial/cad-loader": "^0.3.0",
|
|
41
|
+
"@openusd-wasm/pxr": "0.0.13",
|
|
42
|
+
"@openusd-wasm/three-loader": "^0.0.13"
|
|
40
43
|
},
|
|
41
44
|
"peerDependencies": {
|
|
42
45
|
"three": "0.182.0"
|
|
43
46
|
},
|
|
44
47
|
"devDependencies": {
|
|
45
|
-
"@types/node": "^25.6.0",
|
|
46
48
|
"@types/jsdom": "^27.0.0",
|
|
49
|
+
"@types/node": "^25.6.0",
|
|
47
50
|
"@types/three": "0.182.0",
|
|
51
|
+
"jsdom": "^29.0.2",
|
|
48
52
|
"three": "0.182.0",
|
|
49
53
|
"typescript": "^5.6.3",
|
|
50
|
-
"jsdom": "^29.0.2",
|
|
51
54
|
"vite": "^5.4.10",
|
|
52
55
|
"vitest": "^2.1.8"
|
|
53
56
|
}
|