@nebula-spatial/viewer 0.3.0 → 0.4.1
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 +99 -49
- package/README.md +598 -220
- package/dist/assets/types.d.ts +235 -0
- package/dist/cad/types.d.ts +16 -26
- package/dist/index-D0lW4RQP.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-B4uKXhdV.js +266 -0
- package/dist/session-DNMxzxd5.js +762 -0
- package/dist/stage-CFr9ZkwQ.js +421 -0
- package/dist/types.d.ts +24 -2
- package/dist/ui/types.d.ts +45 -0
- package/dist/usd-session-DDWM5msM.js +522 -0
- package/package.json +57 -54
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
var E = Object.defineProperty;
|
|
2
|
+
var M = (r, t, i) => t in r ? E(r, t, { enumerable: !0, configurable: !0, writable: !0, value: i }) : r[t] = i;
|
|
3
|
+
var p = (r, t, i) => M(r, typeof t != "symbol" ? t + "" : t, i);
|
|
4
|
+
import T from "@mujoco/mujoco";
|
|
5
|
+
import I from "@mujoco/mujoco/mujoco.wasm?url";
|
|
6
|
+
let w = 0;
|
|
7
|
+
function F(r, t) {
|
|
8
|
+
const i = `/viewer-simulation-${++w}`;
|
|
9
|
+
r.mkdir(i);
|
|
10
|
+
const n = /* @__PURE__ */ new Set([i]), o = /* @__PURE__ */ new Set(), a = (e, s) => {
|
|
11
|
+
const c = e.replaceAll("\\", "/").split("/");
|
|
12
|
+
if (c[0] === "" || c.some((u) => u === ".." || u.includes(":"))) throw b(`Invalid package path: ${e}`, "E_SIMULATION_PATH");
|
|
13
|
+
const l = c.filter((u) => u && u !== ".");
|
|
14
|
+
if (!l.length) throw b("Package path is empty.", "E_SIMULATION_PATH");
|
|
15
|
+
let m = i;
|
|
16
|
+
for (const u of l.slice(0, -1))
|
|
17
|
+
m += `/${u}`, n.has(m) || (r.mkdir(m), n.add(m));
|
|
18
|
+
const d = `${i}/${l.join("/")}`;
|
|
19
|
+
return r.writeFile(d, s), o.add(d), d;
|
|
20
|
+
}, h = () => {
|
|
21
|
+
for (const e of o) r.unlink(e);
|
|
22
|
+
o.clear();
|
|
23
|
+
for (const e of [...n].reverse()) r.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: h };
|
|
29
|
+
} catch (e) {
|
|
30
|
+
throw h(), e;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function b(r, t) {
|
|
34
|
+
return Object.assign(new Error(r), { code: t });
|
|
35
|
+
}
|
|
36
|
+
function O({
|
|
37
|
+
target: r,
|
|
38
|
+
point: t,
|
|
39
|
+
velocity: i,
|
|
40
|
+
stiffness: n,
|
|
41
|
+
damping: o,
|
|
42
|
+
maxForce: a,
|
|
43
|
+
strength: h
|
|
44
|
+
}) {
|
|
45
|
+
const e = [
|
|
46
|
+
n * (r[0] - t[0]) - o * i[0],
|
|
47
|
+
n * (r[1] - t[1]) - o * i[1],
|
|
48
|
+
n * (r[2] - t[2]) - o * i[2]
|
|
49
|
+
], s = Math.hypot(e[0], e[1], e[2]), c = a * h;
|
|
50
|
+
if (s > c && s > 0) {
|
|
51
|
+
const l = c / s;
|
|
52
|
+
e[0] *= l, e[1] *= l, e[2] *= l;
|
|
53
|
+
}
|
|
54
|
+
return e;
|
|
55
|
+
}
|
|
56
|
+
const f = 1 / 240, A = 0.05, v = 80, x = 1.15, g = 3.5, y = 14;
|
|
57
|
+
class C {
|
|
58
|
+
constructor() {
|
|
59
|
+
p(this, "mujoco", null);
|
|
60
|
+
p(this, "model", null);
|
|
61
|
+
p(this, "data", null);
|
|
62
|
+
p(this, "grab", null);
|
|
63
|
+
p(this, "lastGrabForce", [0, 0, 0]);
|
|
64
|
+
p(this, "velocityBuffer", new Float64Array(6));
|
|
65
|
+
p(this, "initialControls", []);
|
|
66
|
+
p(this, "accumulator", 0);
|
|
67
|
+
p(this, "initialVelocities", []);
|
|
68
|
+
}
|
|
69
|
+
async initialize(t, i) {
|
|
70
|
+
var h;
|
|
71
|
+
this.mujoco ?? (this.mujoco = await T({ locateFile: (e) => e.endsWith(".wasm") ? i ? new URL(e, i).href : I : e }));
|
|
72
|
+
const n = F(this.mujoco.FS, t.files);
|
|
73
|
+
let o = null, a = null;
|
|
74
|
+
try {
|
|
75
|
+
o = this.mujoco.MjModel.from_xml_path(n.writeEntry(t.xml));
|
|
76
|
+
for (const e of t.massOverrides ?? []) {
|
|
77
|
+
const s = this.mujoco.mj_name2id(o, this.mujoco.mjtObj.mjOBJ_BODY.value, e.body);
|
|
78
|
+
if (s <= 0 || !Number.isFinite(e.mass) || e.mass <= 0 || !(o.body_mass[s] > 0)) throw new Error(`Invalid mass override: ${e.body}`);
|
|
79
|
+
const c = e.mass / o.body_mass[s];
|
|
80
|
+
o.body_mass[s] = e.mass;
|
|
81
|
+
for (let l = 0; l < 3; l++) o.body_inertia[s * 3 + l] *= c;
|
|
82
|
+
}
|
|
83
|
+
a = new this.mujoco.MjData(o), (h = t.massOverrides) != null && h.length && this.mujoco.mj_setConst(o, a), o.opt.timestep = f, this.applyInitialControls(a, t.initialControls), this.applyInitialVelocities(o, a, t.initialVelocities), this.mujoco.mj_forward(o, a);
|
|
84
|
+
} catch (e) {
|
|
85
|
+
throw a == null || a.delete(), o == null || o.delete(), _(e, "E_MUJOCO_INITIALIZE");
|
|
86
|
+
} finally {
|
|
87
|
+
n.dispose();
|
|
88
|
+
}
|
|
89
|
+
this.disposeModel(), this.model = o, this.data = a, this.initialControls = t.initialControls ?? [], this.initialVelocities = t.initialVelocities ?? [], this.accumulator = 0;
|
|
90
|
+
}
|
|
91
|
+
beginGrab({
|
|
92
|
+
bodyId: t,
|
|
93
|
+
localPoint: i,
|
|
94
|
+
target: n,
|
|
95
|
+
stiffness: o = v,
|
|
96
|
+
damping: a,
|
|
97
|
+
maxForce: h,
|
|
98
|
+
dampingRatio: e = x,
|
|
99
|
+
maxTargetSpeed: s = g,
|
|
100
|
+
targetResponse: c = y
|
|
101
|
+
}) {
|
|
102
|
+
var j;
|
|
103
|
+
const l = Math.max(((j = this.model) == null ? void 0 : j.body_mass[t]) ?? 1, 1e-3), m = a ?? 2 * Math.sqrt(o * l) * e, d = h ?? Math.max(120, l * 60), u = [n[0], n[1], n[2]];
|
|
104
|
+
this.grab = {
|
|
105
|
+
bodyId: t,
|
|
106
|
+
localPoint: [i[0], i[1], i[2]],
|
|
107
|
+
target: [...u],
|
|
108
|
+
requestedTarget: u,
|
|
109
|
+
stiffness: o,
|
|
110
|
+
damping: m,
|
|
111
|
+
maxForce: d,
|
|
112
|
+
strength: 1,
|
|
113
|
+
maxTargetSpeed: s,
|
|
114
|
+
targetResponse: c
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
updateGrabTarget(t) {
|
|
118
|
+
this.grab && (this.grab.requestedTarget = [t[0], t[1], t[2]]);
|
|
119
|
+
}
|
|
120
|
+
updateGrabStrength(t) {
|
|
121
|
+
this.grab && (this.grab.strength = t);
|
|
122
|
+
}
|
|
123
|
+
applyGrabForce() {
|
|
124
|
+
if (!this.grab || !this.model || !this.data || !this.mujoco) return;
|
|
125
|
+
const { bodyId: t, localPoint: i, target: n, stiffness: o, damping: a, maxForce: h, strength: e } = this.grab, s = this.data.xmat.subarray(t * 9, t * 9 + 9), c = this.data.xpos.subarray(t * 3, t * 3 + 3), l = [
|
|
126
|
+
c[0] + s[0] * i[0] + s[1] * i[1] + s[2] * i[2],
|
|
127
|
+
c[1] + s[3] * i[0] + s[4] * i[1] + s[5] * i[2],
|
|
128
|
+
c[2] + s[6] * i[0] + s[7] * i[1] + s[8] * i[2]
|
|
129
|
+
];
|
|
130
|
+
this.mujoco.mj_objectVelocity(
|
|
131
|
+
this.model,
|
|
132
|
+
this.data,
|
|
133
|
+
this.mujoco.mjtObj.mjOBJ_BODY.value,
|
|
134
|
+
t,
|
|
135
|
+
this.velocityBuffer,
|
|
136
|
+
0
|
|
137
|
+
);
|
|
138
|
+
const m = this.velocityBuffer, d = [
|
|
139
|
+
l[0] - c[0],
|
|
140
|
+
l[1] - c[1],
|
|
141
|
+
l[2] - c[2]
|
|
142
|
+
], u = [
|
|
143
|
+
m[3] + m[1] * d[2] - m[2] * d[1],
|
|
144
|
+
m[4] + m[2] * d[0] - m[0] * d[2],
|
|
145
|
+
m[5] + m[0] * d[1] - m[1] * d[0]
|
|
146
|
+
], j = O({ target: n, point: l, velocity: u, stiffness: o, damping: a, maxForce: h, strength: e });
|
|
147
|
+
this.lastGrabForce = [...j], this.data.qfrc_applied.fill(0), this.mujoco.mj_applyFT(
|
|
148
|
+
this.model,
|
|
149
|
+
this.data,
|
|
150
|
+
j,
|
|
151
|
+
[0, 0, 0],
|
|
152
|
+
l,
|
|
153
|
+
t,
|
|
154
|
+
this.data.qfrc_applied
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
endGrab() {
|
|
158
|
+
this.data && this.data.qfrc_applied.fill(0), this.lastGrabForce = [0, 0, 0], this.grab = null;
|
|
159
|
+
}
|
|
160
|
+
step(t) {
|
|
161
|
+
if (!this.mujoco || !this.model || !this.data || t <= 0) return !1;
|
|
162
|
+
this.accumulator += Math.min(t, A);
|
|
163
|
+
let i = !1;
|
|
164
|
+
for (; this.accumulator >= f; )
|
|
165
|
+
this.updateGrabTargetForStep(), this.applyGrabForce(), this.mujoco.mj_step(this.model, this.data), this.accumulator -= f, i = !0;
|
|
166
|
+
return i;
|
|
167
|
+
}
|
|
168
|
+
updateGrabTargetForStep() {
|
|
169
|
+
const t = this.grab;
|
|
170
|
+
if (!t) return;
|
|
171
|
+
const i = t.requestedTarget ?? t.target, n = (t.maxTargetSpeed ?? g) * Math.max(1, Math.sqrt(t.strength)), o = t.targetResponse ?? y, a = 1 - Math.exp(-o * f), h = n * f;
|
|
172
|
+
for (let e = 0; e < 3; e++) {
|
|
173
|
+
const s = i[e] - t.target[e], c = Math.sign(s) * Math.min(Math.abs(s) * a, h);
|
|
174
|
+
t.target[e] += c;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
reset() {
|
|
178
|
+
!this.mujoco || !this.model || !this.data || (this.endGrab(), 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);
|
|
179
|
+
}
|
|
180
|
+
dispose() {
|
|
181
|
+
this.disposeModel(), this.mujoco = null;
|
|
182
|
+
}
|
|
183
|
+
applyInitialControls(t, i) {
|
|
184
|
+
if (i != null && i.length) {
|
|
185
|
+
if (i.length !== t.ctrl.length) throw _(new Error(`Initial control count ${i.length} does not match MuJoCo actuator count ${t.ctrl.length}.`), "E_MUJOCO_INITIALIZE");
|
|
186
|
+
t.ctrl.set(i);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
applyInitialVelocities(t, i, n) {
|
|
190
|
+
for (const o of n ?? []) {
|
|
191
|
+
const a = this.mujoco.mj_name2id(t, this.mujoco.mjtObj.mjOBJ_BODY.value, o.body), h = a >= 0 ? t.body_jntadr[a] : -1;
|
|
192
|
+
if (h < 0 || t.jnt_type[h] !== this.mujoco.mjtJoint.mjJNT_FREE.value || o.velocity.length !== 6 || !o.velocity.every(Number.isFinite))
|
|
193
|
+
throw _(new Error(`Invalid free-body initial velocity: ${o.body}`), "E_MUJOCO_INITIALIZE");
|
|
194
|
+
i.qvel.set(o.velocity, t.jnt_dofadr[h]);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
disposeModel() {
|
|
198
|
+
var t, i;
|
|
199
|
+
this.endGrab(), (t = this.data) == null || t.delete(), (i = this.model) == null || i.delete(), this.data = null, this.model = null, this.initialControls = [], this.initialVelocities = [], this.accumulator = 0;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
function _(r, t) {
|
|
203
|
+
const i = r instanceof Error ? r : new Error(String(r));
|
|
204
|
+
return Object.assign(i, { code: t });
|
|
205
|
+
}
|
|
206
|
+
export {
|
|
207
|
+
C as MujocoRuntime
|
|
208
|
+
};
|
package/dist/plugins.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Box3, Object3D } from 'three';
|
|
2
2
|
import type { CadHit, CadSelection, CadViewerSnapshot, CadViewerEventMap } from './cad/types';
|
|
3
|
-
import type { ViewerEventMap, ViewerViewMode } from './types';
|
|
3
|
+
import type { ViewerEventMap, ViewerViewMode, ViewerUiTheme } from './types';
|
|
4
|
+
import type { AssetControlState, AssetLoaderContribution, AssetPreviewSnapshot, AssetUpAxis } from './assets/types';
|
|
4
5
|
export interface Disposable {
|
|
5
6
|
dispose(): void;
|
|
6
7
|
}
|
|
@@ -9,6 +10,9 @@ export interface ToolbarItem {
|
|
|
9
10
|
readonly label: string;
|
|
10
11
|
readonly title?: string;
|
|
11
12
|
readonly disabled?: boolean;
|
|
13
|
+
readonly pressed?: boolean;
|
|
14
|
+
/** Inline SVG markup (24x24 viewBox, `currentColor` stroke). */
|
|
15
|
+
readonly icon?: string;
|
|
12
16
|
readonly onClick: () => void;
|
|
13
17
|
}
|
|
14
18
|
export interface ViewerState {
|
|
@@ -17,10 +21,18 @@ export interface ViewerState {
|
|
|
17
21
|
readonly highlightedObject: Object3D | null;
|
|
18
22
|
readonly isDisposed: boolean;
|
|
19
23
|
}
|
|
24
|
+
/** Read-only view of the active asset. Never exposes the mutable `AssetPreview` handle. */
|
|
25
|
+
export interface ViewerAssetReadApi {
|
|
26
|
+
/** Snapshot of the active asset; `null` when no asset is open. */
|
|
27
|
+
$read(): AssetPreviewSnapshot | null;
|
|
28
|
+
/** Authoritative control state, safe to bind button pressed states to. */
|
|
29
|
+
$readControl(): AssetControlState;
|
|
30
|
+
}
|
|
20
31
|
export interface ViewerPluginContext {
|
|
21
32
|
readonly cad: {
|
|
22
33
|
$read(): CadViewerSnapshot;
|
|
23
34
|
};
|
|
35
|
+
readonly asset: ViewerAssetReadApi;
|
|
24
36
|
readonly viewer: {
|
|
25
37
|
$getState(): ViewerState;
|
|
26
38
|
};
|
|
@@ -29,6 +41,14 @@ export interface ViewerPluginContext {
|
|
|
29
41
|
setSelection(selection: CadSelection | null): void;
|
|
30
42
|
fitDocument(): boolean;
|
|
31
43
|
setViewMode(mode: ViewerViewMode): void;
|
|
44
|
+
play(): boolean;
|
|
45
|
+
pause(): boolean;
|
|
46
|
+
reset(): boolean;
|
|
47
|
+
fitView(): boolean;
|
|
48
|
+
setUpAxis(axis: AssetUpAxis): boolean;
|
|
49
|
+
setGroundVisible(visible: boolean): boolean;
|
|
50
|
+
setCollisionVisible(visible: boolean): boolean;
|
|
51
|
+
setTheme(theme: ViewerUiTheme): boolean;
|
|
32
52
|
};
|
|
33
53
|
readonly events: {
|
|
34
54
|
on<K extends keyof (ViewerEventMap & CadViewerEventMap)>(type: K, listener: (event: (ViewerEventMap & CadViewerEventMap)[K]) => void): () => void;
|
|
@@ -82,7 +102,7 @@ export type ViewerContributionPoint = {
|
|
|
82
102
|
readonly kind: 'fit-policy';
|
|
83
103
|
readonly id: string;
|
|
84
104
|
readonly resolve: (ctx: ViewerPluginContext) => Box3 | null;
|
|
85
|
-
};
|
|
105
|
+
} | AssetLoaderContribution;
|
|
86
106
|
export interface ViewerPlugin {
|
|
87
107
|
readonly id: string;
|
|
88
108
|
activate(context: ViewerPluginContext): void | Promise<void>;
|
|
@@ -90,25 +110,49 @@ export interface ViewerPlugin {
|
|
|
90
110
|
}
|
|
91
111
|
type SnapshotReader = () => CadViewerSnapshot;
|
|
92
112
|
type StateReader = () => ViewerState;
|
|
113
|
+
type AssetReader = () => AssetPreviewSnapshot | null;
|
|
114
|
+
type ControlStateReader = () => AssetControlState;
|
|
93
115
|
type PluginEventMap = ViewerEventMap & CadViewerEventMap;
|
|
94
116
|
type EventSubscriber = <K extends keyof PluginEventMap>(type: K, listener: (event: PluginEventMap[K]) => void) => () => void;
|
|
95
117
|
type CommandApi = ViewerPluginContext['commands'];
|
|
118
|
+
/**
|
|
119
|
+
* Layout channel the plugin manager delegates to. The engine owns zone
|
|
120
|
+
* containers, insets computation and the `ui-layout-change` broadcast; the
|
|
121
|
+
* manager only forwards contribution nodes into it.
|
|
122
|
+
*/
|
|
123
|
+
export interface PluginLayoutPort {
|
|
124
|
+
mountPlugin(node: Node, position?: 'left' | 'right' | 'top' | 'bottom' | 'overlay'): Disposable;
|
|
125
|
+
/** Built-in UI path: the package owns the styling of nodes mounted this way. */
|
|
126
|
+
mountBuiltin(node: Node, options: {
|
|
127
|
+
zone: 'left' | 'right' | 'top' | 'bottom' | 'overlay';
|
|
128
|
+
group?: string;
|
|
129
|
+
}): Disposable;
|
|
130
|
+
}
|
|
96
131
|
export declare class ViewerPluginManager implements ViewerPluginRegistry {
|
|
97
132
|
private readonly readCad;
|
|
98
133
|
private readonly readState;
|
|
134
|
+
private readonly readAsset;
|
|
135
|
+
private readonly readControlState;
|
|
99
136
|
private readonly subscribe;
|
|
100
137
|
private readonly commands;
|
|
101
138
|
private readonly hostElement;
|
|
139
|
+
private readonly layout;
|
|
102
140
|
private readonly pluginsById;
|
|
103
141
|
private readonly contributions;
|
|
104
142
|
private readonly commandContributions;
|
|
143
|
+
private readonly builtinPlugins;
|
|
144
|
+
private readonly contributionRemovedListeners;
|
|
105
145
|
private refreshingInspectors;
|
|
106
146
|
private disposed;
|
|
107
|
-
constructor(readCad: SnapshotReader, readState: StateReader, subscribe: EventSubscriber, commands: CommandApi, hostElement: HTMLElement);
|
|
147
|
+
constructor(readCad: SnapshotReader, readState: StateReader, readAsset: AssetReader, readControlState: ControlStateReader, subscribe: EventSubscriber, commands: CommandApi, hostElement: HTMLElement, layout?: PluginLayoutPort | null);
|
|
108
148
|
register(plugin: ViewerPlugin): Disposable;
|
|
109
149
|
use(plugins: readonly ViewerPlugin[], options?: {
|
|
110
150
|
replace?: boolean;
|
|
111
151
|
}): void;
|
|
152
|
+
registerBuiltin(plugin: ViewerPlugin): Disposable;
|
|
153
|
+
getAssetLoaders(): readonly AssetLoaderContribution[];
|
|
154
|
+
onAssetLoaderRemoved(listener: (loader: AssetLoaderContribution) => void): Disposable;
|
|
155
|
+
private onContributionRemoved;
|
|
112
156
|
private deactivate;
|
|
113
157
|
disposeAll(): void;
|
|
114
158
|
dispose(): void;
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { Mesh as F, MeshPhongMaterial as m, MeshLambertMaterial as D, MeshStandardMaterial as L, Color as A, Quaternion as w, Vector3 as B, Group as E, LoaderUtils as C, Box3 as N } from "three";
|
|
2
|
+
import { DRACOLoader as v } from "three/examples/jsm/loaders/DRACOLoader.js";
|
|
3
|
+
import { FBXLoader as G } from "three/examples/jsm/loaders/FBXLoader.js";
|
|
4
|
+
import { GLTFLoader as _ } from "three/examples/jsm/loaders/GLTFLoader.js";
|
|
5
|
+
import { S as R, d as O } from "./stage-CFr9ZkwQ.js";
|
|
6
|
+
const b = "Kaydara FBX Binary \0", X = /(?:^|[\r\n])\s*P:\s*"UpAxis"\s*,\s*"int"\s*,\s*"Integer"\s*,\s*""\s*,\s*(-?\d+)/i, z = /(?:^|[\r\n])\s*P:\s*"UpAxisSign"\s*,\s*"int"\s*,\s*"Integer"\s*,\s*""\s*,\s*(-?\d+)/i;
|
|
7
|
+
function V(e) {
|
|
8
|
+
try {
|
|
9
|
+
const n = new Uint8Array(e), r = q(n) ? Y(e) : W(new TextDecoder().decode(n));
|
|
10
|
+
return !r || r.sign !== 1 ? null : r.axis === 1 ? "y" : r.axis === 2 ? "z" : null;
|
|
11
|
+
} catch {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function q(e) {
|
|
16
|
+
if (e.byteLength < b.length) return !1;
|
|
17
|
+
for (let n = 0; n < b.length; n += 1)
|
|
18
|
+
if (e[n] !== b.charCodeAt(n)) return !1;
|
|
19
|
+
return !0;
|
|
20
|
+
}
|
|
21
|
+
function W(e) {
|
|
22
|
+
var t, i;
|
|
23
|
+
const n = Number.parseInt(((t = X.exec(e)) == null ? void 0 : t[1]) ?? "", 10);
|
|
24
|
+
if (!Number.isInteger(n)) return null;
|
|
25
|
+
const r = (i = z.exec(e)) == null ? void 0 : i[1], s = r === void 0 ? 1 : Number.parseInt(r, 10);
|
|
26
|
+
return Number.isInteger(s) ? { axis: n, sign: s } : null;
|
|
27
|
+
}
|
|
28
|
+
function Y(e) {
|
|
29
|
+
const n = new DataView(e);
|
|
30
|
+
if (n.byteLength < 27) return null;
|
|
31
|
+
const r = n.getUint32(23, !0) >= 7500;
|
|
32
|
+
let s = 27;
|
|
33
|
+
for (; s < n.byteLength; ) {
|
|
34
|
+
const t = T(n, s, r, !1);
|
|
35
|
+
if (!t) break;
|
|
36
|
+
if (t.name === "GlobalSettings") return P(n, t.childrenOffset, t.endOffset, r);
|
|
37
|
+
s = t.endOffset;
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
function P(e, n, r, s) {
|
|
42
|
+
let t = n, i = null, l = 1;
|
|
43
|
+
for (; t < r; ) {
|
|
44
|
+
const a = T(e, t, s, !0);
|
|
45
|
+
if (!a) break;
|
|
46
|
+
if (a.name === "P") {
|
|
47
|
+
const [u, , , , o] = a.properties;
|
|
48
|
+
u === "UpAxis" && typeof o == "number" && (i = o), u === "UpAxisSign" && typeof o == "number" && (l = o);
|
|
49
|
+
}
|
|
50
|
+
if (a.childrenOffset < a.endOffset) {
|
|
51
|
+
const u = P(e, a.childrenOffset, a.endOffset, s);
|
|
52
|
+
u && (i ?? (i = u.axis), l = u.sign);
|
|
53
|
+
}
|
|
54
|
+
t = a.endOffset;
|
|
55
|
+
}
|
|
56
|
+
return i === null ? null : { axis: i, sign: l };
|
|
57
|
+
}
|
|
58
|
+
function T(e, n, r, s) {
|
|
59
|
+
const t = r ? 25 : 13;
|
|
60
|
+
if (n + t > e.byteLength) return null;
|
|
61
|
+
const i = M(e, n, r), l = M(e, n + (r ? 8 : 4), r), a = M(e, n + (r ? 16 : 8), r), u = e.getUint8(n + (r ? 24 : 12));
|
|
62
|
+
if (i === 0) return null;
|
|
63
|
+
const o = n + t, c = o + u, f = c + a;
|
|
64
|
+
if (i > e.byteLength || f > i) return null;
|
|
65
|
+
const d = new TextDecoder().decode(new Uint8Array(e.buffer, e.byteOffset + o, u)), p = [];
|
|
66
|
+
if (s) {
|
|
67
|
+
let h = c;
|
|
68
|
+
for (let y = 0; y < l; y += 1) {
|
|
69
|
+
const g = $(e, h);
|
|
70
|
+
if (p.push(g.value), h = g.nextOffset, h > f) return null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return { name: d, endOffset: i, childrenOffset: f, properties: p };
|
|
74
|
+
}
|
|
75
|
+
function M(e, n, r) {
|
|
76
|
+
if (!r) return e.getUint32(n, !0);
|
|
77
|
+
const s = e.getBigUint64(n, !0);
|
|
78
|
+
if (s > BigInt(Number.MAX_SAFE_INTEGER)) throw new Error("FBX offset exceeds the safe integer range.");
|
|
79
|
+
return Number(s);
|
|
80
|
+
}
|
|
81
|
+
function $(e, n) {
|
|
82
|
+
const r = String.fromCharCode(e.getUint8(n));
|
|
83
|
+
switch (r) {
|
|
84
|
+
case "Y":
|
|
85
|
+
return { value: e.getInt16(n + 1, !0), nextOffset: n + 3 };
|
|
86
|
+
case "C":
|
|
87
|
+
return { value: e.getUint8(n + 1) !== 0, nextOffset: n + 2 };
|
|
88
|
+
case "I":
|
|
89
|
+
return { value: e.getInt32(n + 1, !0), nextOffset: n + 5 };
|
|
90
|
+
case "F":
|
|
91
|
+
return { value: e.getFloat32(n + 1, !0), nextOffset: n + 5 };
|
|
92
|
+
case "D":
|
|
93
|
+
return { value: e.getFloat64(n + 1, !0), nextOffset: n + 9 };
|
|
94
|
+
case "L":
|
|
95
|
+
return { value: Number(e.getBigInt64(n + 1, !0)), nextOffset: n + 9 };
|
|
96
|
+
case "S":
|
|
97
|
+
case "R": {
|
|
98
|
+
const s = e.getUint32(n + 1, !0), t = n + 5, i = new Uint8Array(e.buffer, e.byteOffset + t, s);
|
|
99
|
+
return { value: r === "S" ? new TextDecoder().decode(i) : i, nextOffset: t + s };
|
|
100
|
+
}
|
|
101
|
+
case "f":
|
|
102
|
+
case "d":
|
|
103
|
+
case "l":
|
|
104
|
+
case "i":
|
|
105
|
+
case "b":
|
|
106
|
+
case "c": {
|
|
107
|
+
const s = e.getUint32(n + 9, !0);
|
|
108
|
+
return { value: null, nextOffset: n + 13 + s };
|
|
109
|
+
}
|
|
110
|
+
default:
|
|
111
|
+
throw new Error(`Unsupported FBX property type: ${r}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function j(e) {
|
|
115
|
+
const n = /* @__PURE__ */ new Map();
|
|
116
|
+
e.traverse((r) => {
|
|
117
|
+
r instanceof F && (Array.isArray(r.material) ? r.material = r.material.map((s) => S(s, n)) : r.material = S(r.material, n));
|
|
118
|
+
});
|
|
119
|
+
for (const [r, s] of n)
|
|
120
|
+
r !== s && r.dispose();
|
|
121
|
+
}
|
|
122
|
+
function S(e, n) {
|
|
123
|
+
var i, l, a;
|
|
124
|
+
const r = n.get(e);
|
|
125
|
+
if (r) return r;
|
|
126
|
+
if (!(e instanceof m) && !(e instanceof D))
|
|
127
|
+
return n.set(e, e), e;
|
|
128
|
+
const s = e instanceof m ? e : null, t = new L({
|
|
129
|
+
name: e.name,
|
|
130
|
+
color: ((i = e.color) == null ? void 0 : i.clone()) ?? new A(16777215),
|
|
131
|
+
map: e.map,
|
|
132
|
+
emissive: ((l = e.emissive) == null ? void 0 : l.clone()) ?? new A(0),
|
|
133
|
+
emissiveMap: e.emissiveMap,
|
|
134
|
+
emissiveIntensity: e.emissiveIntensity,
|
|
135
|
+
normalMap: e.normalMap,
|
|
136
|
+
normalMapType: e.normalMapType,
|
|
137
|
+
normalScale: (a = e.normalScale) == null ? void 0 : a.clone(),
|
|
138
|
+
bumpMap: e.bumpMap,
|
|
139
|
+
bumpScale: e.bumpScale,
|
|
140
|
+
displacementMap: e.displacementMap,
|
|
141
|
+
displacementScale: e.displacementScale,
|
|
142
|
+
displacementBias: e.displacementBias,
|
|
143
|
+
aoMap: e.aoMap,
|
|
144
|
+
aoMapIntensity: e.aoMapIntensity,
|
|
145
|
+
alphaMap: e.alphaMap,
|
|
146
|
+
opacity: e.opacity,
|
|
147
|
+
transparent: e.transparent,
|
|
148
|
+
alphaTest: e.alphaTest,
|
|
149
|
+
side: e.side,
|
|
150
|
+
depthTest: e.depthTest,
|
|
151
|
+
depthWrite: e.depthWrite,
|
|
152
|
+
vertexColors: e.vertexColors,
|
|
153
|
+
wireframe: e.wireframe,
|
|
154
|
+
flatShading: e.flatShading,
|
|
155
|
+
metalness: 0,
|
|
156
|
+
// Approximate the Blinn-Phong exponent as perceptual roughness. This keeps
|
|
157
|
+
// glossy FBX materials glossy without pretending they carry metalness data.
|
|
158
|
+
roughness: s ? Math.min(1, Math.max(0.04, Math.sqrt(2 / (s.shininess + 2)))) : 1
|
|
159
|
+
});
|
|
160
|
+
return t.userData = { ...e.userData }, t.visible = e.visible, t.toneMapped = e.toneMapped, t.clippingPlanes = e.clippingPlanes, t.clipIntersection = e.clipIntersection, t.clipShadows = e.clipShadows, t.polygonOffset = e.polygonOffset, t.polygonOffsetFactor = e.polygonOffsetFactor, t.polygonOffsetUnits = e.polygonOffsetUnits, t.dithering = e.dithering, t.needsUpdate = !0, n.set(e, t), t;
|
|
161
|
+
}
|
|
162
|
+
const k = new w().setFromAxisAngle(new B(1, 0, 0), Math.PI / 2), Q = new w();
|
|
163
|
+
function U(e) {
|
|
164
|
+
return e === "fbx" ? "y" : "z";
|
|
165
|
+
}
|
|
166
|
+
function K(e) {
|
|
167
|
+
return e === "y" ? k : Q;
|
|
168
|
+
}
|
|
169
|
+
function I(e, n) {
|
|
170
|
+
const r = K(n);
|
|
171
|
+
return r.equals(e.quaternion) ? !1 : (e.quaternion.copy(r), e.updateMatrixWorld(!0), !0);
|
|
172
|
+
}
|
|
173
|
+
function re(e, n) {
|
|
174
|
+
let r = !1, s = null, t = null, i = "z", l;
|
|
175
|
+
const a = () => {
|
|
176
|
+
r = !0;
|
|
177
|
+
}, u = (async () => {
|
|
178
|
+
i = n.options.upAxis ?? U(n.format);
|
|
179
|
+
let o = null;
|
|
180
|
+
try {
|
|
181
|
+
if (r || n.signal.aborted) throw x();
|
|
182
|
+
const c = e.url ?? await e.resolve(e.entryPath ?? "");
|
|
183
|
+
if (o = new E(), o.name = "viewer-model-root", n.format === "fbx") {
|
|
184
|
+
const d = await fetch(c, { signal: n.signal });
|
|
185
|
+
if (!d.ok) throw new Error(`FBX request failed: ${d.status} ${d.statusText}`);
|
|
186
|
+
const p = await d.arrayBuffer();
|
|
187
|
+
i = n.options.upAxis ?? V(p) ?? U(n.format), o.add(new G().parse(p, C.extractUrlBase(c))), j(o);
|
|
188
|
+
} else {
|
|
189
|
+
const d = new v(), p = new _();
|
|
190
|
+
try {
|
|
191
|
+
n.options.dracoDecoderPath && d.setDecoderPath(n.options.dracoDecoderPath), p.setDRACOLoader(d);
|
|
192
|
+
const h = await p.loadAsync(c), y = h.scenes.filter((g) => g !== h.scene);
|
|
193
|
+
if (o.add(h.scene), r || n.signal.aborted) {
|
|
194
|
+
for (const g of y) o.add(g);
|
|
195
|
+
throw x();
|
|
196
|
+
}
|
|
197
|
+
} finally {
|
|
198
|
+
d.dispose();
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (r || n.signal.aborted) throw x();
|
|
202
|
+
I(o, i), Z(o);
|
|
203
|
+
const f = new R(o, n.options.simulationTheme, void 0, "model");
|
|
204
|
+
s = o, t = f, o = null;
|
|
205
|
+
} finally {
|
|
206
|
+
o && O(o);
|
|
207
|
+
}
|
|
208
|
+
})();
|
|
209
|
+
return u.catch(() => {
|
|
210
|
+
}), {
|
|
211
|
+
id: `model-${Math.random().toString(36).slice(2)}`,
|
|
212
|
+
opened: u,
|
|
213
|
+
get root() {
|
|
214
|
+
return (t == null ? void 0 : t.root) ?? null;
|
|
215
|
+
},
|
|
216
|
+
getFitBounds() {
|
|
217
|
+
return (t == null ? void 0 : t.getModelBounds()) ?? null;
|
|
218
|
+
},
|
|
219
|
+
beforeRender(o, c, f) {
|
|
220
|
+
return t == null ? void 0 : t.beforeRender(o, c, f);
|
|
221
|
+
},
|
|
222
|
+
capabilities: { canPlay: !1, canPause: !1, canReset: !1, hasPhysics: !1, canReorientUpAxis: !0, canToggleGround: !0 },
|
|
223
|
+
// Session presentation overrides are only consulted for `interactionMode`
|
|
224
|
+
// and `fitPolicy`; the UI group comes from the loader descriptor. Keep the
|
|
225
|
+
// two in sync so the override never reads as a contradictory value.
|
|
226
|
+
presentation: { preferredViewMode: "3d", interactionMode: "object", uiProfile: "model", fitPolicy: "fit-on-ready" },
|
|
227
|
+
getUpAxis() {
|
|
228
|
+
return s ? i : null;
|
|
229
|
+
},
|
|
230
|
+
setTheme(o) {
|
|
231
|
+
t == null || t.setTheme(o);
|
|
232
|
+
},
|
|
233
|
+
setGroundVisible(o) {
|
|
234
|
+
t == null || t.setGroundVisible(o);
|
|
235
|
+
},
|
|
236
|
+
getGroundVisible() {
|
|
237
|
+
return (t == null ? void 0 : t.getGroundVisible()) ?? !0;
|
|
238
|
+
},
|
|
239
|
+
setUpAxis(o) {
|
|
240
|
+
if (r || !s || !t || !I(s, o)) return;
|
|
241
|
+
i = o, t.reseatGround();
|
|
242
|
+
const c = t.getModelBounds();
|
|
243
|
+
n.requestRender(), n.fit(c, { padding: 1.15 });
|
|
244
|
+
},
|
|
245
|
+
beginDispose: a,
|
|
246
|
+
dispose() {
|
|
247
|
+
return a(), l ?? (l = u.catch(() => {
|
|
248
|
+
}).then(() => {
|
|
249
|
+
t == null || t.dispose(), t = null, s && O(s), s = null;
|
|
250
|
+
})), l;
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
function Z(e) {
|
|
255
|
+
e.updateMatrixWorld(!0);
|
|
256
|
+
const n = new N().setFromObject(e);
|
|
257
|
+
if (n.isEmpty()) throw new Error("模型不包含可显示的几何内容。");
|
|
258
|
+
const r = n.getSize(new B()), s = Math.max(r.x, r.y, r.z);
|
|
259
|
+
if (!Number.isFinite(s) || s <= 0) throw new Error("模型尺寸无效。");
|
|
260
|
+
}
|
|
261
|
+
function x() {
|
|
262
|
+
return Object.assign(new Error("Model load aborted."), { name: "AbortError", code: "E_ABORTED" });
|
|
263
|
+
}
|
|
264
|
+
export {
|
|
265
|
+
re as createModelSession
|
|
266
|
+
};
|