@vctrl/hooks 0.25.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +58 -0
- package/README.md +110 -67
- package/index.cjs +1 -0
- package/index.d.ts +2 -1
- package/index.es.js +4 -4
- package/package.json +18 -10
- package/{chunk-CMqjfN_6.cjs → rolldown-runtime-BocRIvOZ.cjs} +1 -1
- package/use-export-model/use-export-model.d.ts +1 -1
- package/use-export-model-CORoAKbf.cjs +1 -0
- package/{use-export-model-efFsSWe-.js → use-export-model-DXesLypI.js} +1 -1
- package/use-export-model.cjs +1 -0
- package/use-export-model.es.js +1 -1
- package/use-load-model/error-helpers.d.ts +5 -0
- package/use-load-model/file-loaders.d.ts +11 -12
- package/use-load-model/index.d.ts +1 -1
- package/use-load-model/load-context.d.ts +17 -0
- package/use-load-model/optimizer-ingest.d.ts +8 -0
- package/use-load-model/optimizer-integration.d.ts +3 -4
- package/use-load-model/scene-loaders.d.ts +15 -0
- package/use-load-model/state.d.ts +14 -14
- package/use-load-model/types.d.ts +154 -131
- package/use-load-model/use-load-model.d.ts +12 -8
- package/use-load-model/utils/index.d.ts +1 -1
- package/use-load-model/utils/reconstruct-files.d.ts +27 -5
- package/use-load-model/utils/resolve-scene-payload.d.ts +9 -0
- package/use-load-model-CRDVzrPC.js +635 -0
- package/use-load-model-CZCi6bPM.cjs +1 -0
- package/use-load-model.cjs +1 -0
- package/use-load-model.es.js +1 -1
- package/use-optimize-model/index.d.ts +1 -0
- package/use-optimize-model/types.d.ts +3 -1
- package/use-optimize-model/use-optimize-model.d.ts +11 -6
- package/use-optimize-model-Fwew6qD8.cjs +1 -0
- package/{use-optimize-model-D16-4Zwm.js → use-optimize-model-sNXkORNg.js} +94 -76
- package/use-optimize-model.cjs +1 -0
- package/use-optimize-model.es.js +2 -2
- package/utils/server-communication.d.ts +0 -5
- package/index.cjs.js +0 -1
- package/use-export-model-yQWV3O3H.cjs +0 -1
- package/use-export-model.cjs.js +0 -1
- package/use-load-model/event-system.d.ts +0 -7
- package/use-load-model-B8oqou4I.cjs +0 -1
- package/use-load-model-C88r8-HE.js +0 -585
- package/use-load-model.cjs.js +0 -1
- package/use-optimize-model-CD5zqTLK.cjs +0 -1
- package/use-optimize-model.cjs.js +0 -1
|
@@ -0,0 +1,635 @@
|
|
|
1
|
+
import { ModelFileTypes as e, ModelLoader as t, isMissingAssetsError as n, isThreeSourceFormat as r, missingAssetsError as i, referenceIn as a, resolutionKey as o, selectionKey as s } from "@vctrl/core/model-loader";
|
|
2
|
+
import { useCallback as c, useMemo as l, useRef as u, useState as d } from "react";
|
|
3
|
+
import { IMPORTABLE_FORMAT_IDS as f, modelFormatForFileName as p } from "@vctrl/core/model-formats";
|
|
4
|
+
import { PERSISTED_BAKE_FILENAME as m, buildAssetLookupKeys as h, getSerializedAssetByteSize as g, isValidBase64 as _, normalizeAssetUri as v, normalizeCameraSettings as y, normalizeSceneInteractions as b, toSerializedAssetBytes as x } from "@vctrl/core";
|
|
5
|
+
//#region packages/hooks/src/use-load-model/error-helpers.ts
|
|
6
|
+
var S = (e) => typeof e == "object" && !!e && "code" in e && "recoverable" in e && "source" in e, C = ({ code: e, message: t, recoverable: n, source: r, cause: i, context: a }) => ({
|
|
7
|
+
code: e,
|
|
8
|
+
message: t,
|
|
9
|
+
recoverable: n,
|
|
10
|
+
source: r,
|
|
11
|
+
cause: i,
|
|
12
|
+
context: a
|
|
13
|
+
}), w = (e) => e instanceof Error && e.message ? e.message : typeof e == "string" && e.trim().length > 0 ? e : "Unknown error", T = (e, t, r) => S(e) ? e : C({
|
|
14
|
+
code: n(e) ? "missing_assets" : t,
|
|
15
|
+
message: w(e),
|
|
16
|
+
recoverable: !0,
|
|
17
|
+
source: "local-upload",
|
|
18
|
+
cause: e,
|
|
19
|
+
context: r
|
|
20
|
+
}), E = (e, t) => {
|
|
21
|
+
if (S(e)) return e;
|
|
22
|
+
let r = w(e), i = n(e) ? "missing_assets" : "server_load_failed";
|
|
23
|
+
return r.includes("Server responded with 404") ? i = "not_found" : (r.includes("Server responded with 402") || r.includes("Server responded with 403")) && (i = "quota_exceeded"), C({
|
|
24
|
+
code: i,
|
|
25
|
+
message: r,
|
|
26
|
+
recoverable: i !== "not_found",
|
|
27
|
+
source: "server-load",
|
|
28
|
+
cause: e,
|
|
29
|
+
context: { sceneId: t }
|
|
30
|
+
});
|
|
31
|
+
}, D = async (e, t) => {
|
|
32
|
+
try {
|
|
33
|
+
await e();
|
|
34
|
+
return;
|
|
35
|
+
} catch (e) {
|
|
36
|
+
console.warn("Optimizer ingest failed.", e);
|
|
37
|
+
}
|
|
38
|
+
if (t) try {
|
|
39
|
+
await t();
|
|
40
|
+
} catch (e) {
|
|
41
|
+
console.warn("Optimizer fallback import failed; optimization is unavailable for this model.", e);
|
|
42
|
+
}
|
|
43
|
+
}, O = (e) => {
|
|
44
|
+
let t = /* @__PURE__ */ new Set(), n = e, r = Array.isArray(n.images) ? n.images : [];
|
|
45
|
+
for (let e of r) typeof e.uri == "string" && !e.uri.startsWith("data:") && t.add(e.uri);
|
|
46
|
+
let i = Array.isArray(n.buffers) ? n.buffers : [];
|
|
47
|
+
for (let e of i) typeof e.uri == "string" && !e.uri.startsWith("data:") && t.add(e.uri);
|
|
48
|
+
return t;
|
|
49
|
+
}, k = async (e, t) => {
|
|
50
|
+
let n = JSON.parse(await e.text()), r = O(n), o = {}, c = /* @__PURE__ */ new Map(), l = /* @__PURE__ */ new Map(), u = /* @__PURE__ */ new Set(), d = async (e) => {
|
|
51
|
+
let t = l.get(e);
|
|
52
|
+
if (t) return t;
|
|
53
|
+
let n = new Uint8Array(await e.arrayBuffer());
|
|
54
|
+
return l.set(e, n), n;
|
|
55
|
+
};
|
|
56
|
+
for (let e of t) c.set(s(e), e);
|
|
57
|
+
let f = s(e), p = [];
|
|
58
|
+
for (let e of r) {
|
|
59
|
+
let t = v(e), n = a(c, e, f);
|
|
60
|
+
if (!n) {
|
|
61
|
+
p.push(e);
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
u.add(n);
|
|
65
|
+
let r = await d(n);
|
|
66
|
+
o[t] = {
|
|
67
|
+
data: Array.from(r),
|
|
68
|
+
fileName: t,
|
|
69
|
+
mimeType: n.type || "application/octet-stream"
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
if (p.length > 0) throw i(`Scene payload is missing required referenced assets: ${p.slice(0, 5).join(", ")}`);
|
|
73
|
+
for (let [e, n] of t.entries()) {
|
|
74
|
+
if (u.has(n)) continue;
|
|
75
|
+
let t = v(n.name), r = t.split("/").pop() || t, i = await d(n);
|
|
76
|
+
o[`extra-${e}-${r}`] = {
|
|
77
|
+
data: Array.from(i),
|
|
78
|
+
fileName: r,
|
|
79
|
+
mimeType: n.type || "application/octet-stream"
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
gltfJson: n,
|
|
84
|
+
assetData: o
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
//#endregion
|
|
88
|
+
//#region packages/hooks/src/use-load-model/utils/read-directory.ts
|
|
89
|
+
async function ee(e) {
|
|
90
|
+
let t = [];
|
|
91
|
+
async function* n(e, t) {
|
|
92
|
+
let r = e;
|
|
93
|
+
for await (let [e, i] of r) i.kind === "file" ? yield te(await i.getFile(), `${t}${e}`) : i.kind === "directory" && (yield* n(i, `${t}${e}/`));
|
|
94
|
+
}
|
|
95
|
+
for await (let r of n(e, `${e.name}/`)) t.push(r);
|
|
96
|
+
return t;
|
|
97
|
+
}
|
|
98
|
+
function te(e, t) {
|
|
99
|
+
return Object.defineProperty(e, "webkitRelativePath", {
|
|
100
|
+
value: t,
|
|
101
|
+
configurable: !0,
|
|
102
|
+
enumerable: !0
|
|
103
|
+
}), e;
|
|
104
|
+
}
|
|
105
|
+
//#endregion
|
|
106
|
+
//#region packages/hooks/src/use-load-model/utils/reconstruct-files.ts
|
|
107
|
+
function A(e) {
|
|
108
|
+
let t = (e ?? "").replace(/[\\/]+/g, "_").replace(/^\.+/, "");
|
|
109
|
+
return t ? `${t}.gltf` : "scene.gltf";
|
|
110
|
+
}
|
|
111
|
+
function j(e) {
|
|
112
|
+
let t = [];
|
|
113
|
+
if (e.assetData && typeof e.assetData == "object") for (let [, n] of Object.entries(e.assetData)) {
|
|
114
|
+
let { fileName: e, mimeType: r } = n;
|
|
115
|
+
if (e === m) continue;
|
|
116
|
+
let i = x(n), a = new Uint8Array(i.byteLength);
|
|
117
|
+
a.set(i);
|
|
118
|
+
let o = new Blob([a], { type: r }), s = new File([o], e, { type: r });
|
|
119
|
+
t.push(s);
|
|
120
|
+
}
|
|
121
|
+
if (!e.gltfJson) throw Error("Cannot reconstruct glTF files: this scene has no glTF document.");
|
|
122
|
+
let n = JSON.stringify(e.gltfJson), r = new Blob([n], { type: "model/gltf+json" }), i = A(e.meta?.name);
|
|
123
|
+
return [new File([r], i, { type: "model/gltf+json" }), ...t];
|
|
124
|
+
}
|
|
125
|
+
//#endregion
|
|
126
|
+
//#region packages/hooks/src/use-load-model/utils/calculate-referenced-bytes.ts
|
|
127
|
+
var ne = (e) => {
|
|
128
|
+
let t = /* @__PURE__ */ new Map();
|
|
129
|
+
for (let n of Object.values(e.assetData || {})) t.set(o(n.fileName), g(n.data));
|
|
130
|
+
return (e) => a(t, e) ?? 0;
|
|
131
|
+
}, re = (e, t) => {
|
|
132
|
+
let n = /* @__PURE__ */ new Map();
|
|
133
|
+
for (let e of t) n.set(s(e), e.size);
|
|
134
|
+
let r = s(e);
|
|
135
|
+
return (e) => a(n, e, r) ?? 0;
|
|
136
|
+
}, M = (e) => {
|
|
137
|
+
let t = /* @__PURE__ */ new Set(), n = /* @__PURE__ */ new Set(), r = e, i = Array.isArray(r.images) ? r.images : [];
|
|
138
|
+
for (let e of i) typeof e.uri == "string" && !e.uri.startsWith("data:") && n.add(e.uri);
|
|
139
|
+
let a = Array.isArray(r.buffers) ? r.buffers : [];
|
|
140
|
+
for (let e of a) typeof e.uri == "string" && !e.uri.startsWith("data:") && t.add(e.uri);
|
|
141
|
+
return {
|
|
142
|
+
bufferUris: t,
|
|
143
|
+
imageUris: n
|
|
144
|
+
};
|
|
145
|
+
};
|
|
146
|
+
async function ie(e, t) {
|
|
147
|
+
let n = await e.text(), r;
|
|
148
|
+
try {
|
|
149
|
+
r = JSON.parse(n);
|
|
150
|
+
} catch {
|
|
151
|
+
return {
|
|
152
|
+
sourcePackageBytes: e.size,
|
|
153
|
+
textureBytes: 0
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
let i = re(e, t), { bufferUris: a, imageUris: o } = M(r), s = Array.from(a).reduce((e, t) => e + i(t), 0), c = Array.from(o).reduce((e, t) => e + i(t), 0);
|
|
157
|
+
return {
|
|
158
|
+
sourcePackageBytes: e.size + s + c,
|
|
159
|
+
textureBytes: c
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
function ae(e) {
|
|
163
|
+
let t = e.gltfJson;
|
|
164
|
+
if (!t) return {
|
|
165
|
+
sourcePackageBytes: 0,
|
|
166
|
+
textureBytes: 0
|
|
167
|
+
};
|
|
168
|
+
let n = ne(e), r = new TextEncoder().encode(JSON.stringify(t)).byteLength, { bufferUris: i, imageUris: a } = M(t), o = Array.from(i).reduce((e, t) => e + n(t), 0), s = Array.from(a).reduce((e, t) => e + n(t), 0);
|
|
169
|
+
return {
|
|
170
|
+
sourcePackageBytes: r + o + s,
|
|
171
|
+
textureBytes: s
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
//#endregion
|
|
175
|
+
//#region packages/hooks/src/use-load-model/utils/resolve-scene-payload.ts
|
|
176
|
+
function N(e) {
|
|
177
|
+
return typeof e == "object" && !!e && !Array.isArray(e);
|
|
178
|
+
}
|
|
179
|
+
function P(e) {
|
|
180
|
+
let t = N(e.settings) ? e.settings : {}, n = y(e.camera ?? t.camera);
|
|
181
|
+
return {
|
|
182
|
+
...t,
|
|
183
|
+
bounds: e.bounds ?? t.bounds,
|
|
184
|
+
camera: n,
|
|
185
|
+
interactions: b(t.interactions, { camera: n }),
|
|
186
|
+
controls: e.controls ?? t.controls,
|
|
187
|
+
environment: e.environment ?? t.environment,
|
|
188
|
+
shadows: e.shadows ?? t.shadows
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
function F(e) {
|
|
192
|
+
let t = /* @__PURE__ */ new Set(), n = e, r = Array.isArray(n.images) ? n.images : [];
|
|
193
|
+
for (let e of r) typeof e.uri == "string" && !e.uri.startsWith("data:") && t.add(e.uri);
|
|
194
|
+
let i = Array.isArray(n.buffers) ? n.buffers : [];
|
|
195
|
+
for (let e of i) typeof e.uri == "string" && !e.uri.startsWith("data:") && t.add(e.uri);
|
|
196
|
+
return t;
|
|
197
|
+
}
|
|
198
|
+
function I(e) {
|
|
199
|
+
for (let [t, n] of Object.entries(e)) {
|
|
200
|
+
if (!n || typeof n != "object") throw Error(`Scene payload contains invalid asset entry for ${t}`);
|
|
201
|
+
if (typeof n.fileName != "string" || n.fileName.trim().length === 0) throw Error(`Scene payload asset ${t} is missing a valid fileName`);
|
|
202
|
+
if (typeof n.mimeType != "string" || n.mimeType.trim().length === 0) throw Error(`Scene payload asset ${t} is missing a valid mimeType`);
|
|
203
|
+
if (typeof n.data == "string" && n.encoding === "base64" && !_(n.data)) throw Error(`Scene payload asset ${n.fileName} has invalid base64 data`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
function L(e, t) {
|
|
207
|
+
let n = F(e);
|
|
208
|
+
if (n.size === 0) return;
|
|
209
|
+
let r = /* @__PURE__ */ new Set();
|
|
210
|
+
for (let e of Object.values(t)) for (let t of h(e.fileName)) r.add(t);
|
|
211
|
+
let a = [];
|
|
212
|
+
for (let e of n) {
|
|
213
|
+
let t = v(e), n = t.split("/").pop() || t;
|
|
214
|
+
!r.has(e) && !r.has(t) && !r.has(n) && a.push(e);
|
|
215
|
+
}
|
|
216
|
+
if (a.length > 0) throw i(`Scene payload is missing required referenced assets: ${a.slice(0, 5).join(", ")}`);
|
|
217
|
+
}
|
|
218
|
+
function R(e) {
|
|
219
|
+
if (!N(e.gltfJson)) throw Error("Scene payload is missing a valid glTF document");
|
|
220
|
+
let t = e.assetData ?? {};
|
|
221
|
+
return I(t), L(e.gltfJson, t), {
|
|
222
|
+
meta: e.meta,
|
|
223
|
+
gltfJson: e.gltfJson,
|
|
224
|
+
assetData: t,
|
|
225
|
+
...P(e)
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
function z(e) {
|
|
229
|
+
let t = e.assetData ?? {};
|
|
230
|
+
return I(t), {
|
|
231
|
+
meta: e.meta,
|
|
232
|
+
gltfJson: null,
|
|
233
|
+
assetData: t,
|
|
234
|
+
...P(e)
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
//#endregion
|
|
238
|
+
//#region packages/hooks/src/use-load-model/utils/fetch-manifest-assets.ts
|
|
239
|
+
async function B(e, t = {}) {
|
|
240
|
+
let n = Object.entries(e);
|
|
241
|
+
if (n.length === 0) return t.onProgress?.(1), {};
|
|
242
|
+
let r = n.every(([, e]) => e.byteSize != null), i = r ? n.reduce((e, [, t]) => e + (t.byteSize ?? 0), 0) : 0, a = 0, o = 0, s = {}, c = new AbortController();
|
|
243
|
+
return await Promise.all(n.map(async ([e, l]) => {
|
|
244
|
+
try {
|
|
245
|
+
let u = await fetch(l.url, {
|
|
246
|
+
headers: t.headers,
|
|
247
|
+
credentials: "same-origin",
|
|
248
|
+
signal: c.signal
|
|
249
|
+
});
|
|
250
|
+
if (!u.ok) throw c.abort(), Error(`Failed to fetch scene asset ${l.fileName}: ${u.status}`);
|
|
251
|
+
let d = new Uint8Array(await u.arrayBuffer());
|
|
252
|
+
s[e] = {
|
|
253
|
+
data: d,
|
|
254
|
+
fileName: l.fileName,
|
|
255
|
+
mimeType: l.mimeType
|
|
256
|
+
}, o += 1, a += l.byteSize ?? 0, t.onProgress?.(r && i > 0 ? a / i : o / n.length);
|
|
257
|
+
} catch (e) {
|
|
258
|
+
throw c.abort(), e;
|
|
259
|
+
}
|
|
260
|
+
})), s;
|
|
261
|
+
}
|
|
262
|
+
//#endregion
|
|
263
|
+
//#region packages/hooks/src/use-load-model/file-loaders.ts
|
|
264
|
+
var V = (e) => {
|
|
265
|
+
let t = /* @__PURE__ */ new Map();
|
|
266
|
+
for (let n of e) {
|
|
267
|
+
let e = p(n.name);
|
|
268
|
+
!e?.canImport || t.has(e.id) || t.set(e.id, n);
|
|
269
|
+
}
|
|
270
|
+
return f.map((e) => t.get(e)).filter((e) => !!e);
|
|
271
|
+
}, H = (e) => {
|
|
272
|
+
if (e.webkitRelativePath) return e;
|
|
273
|
+
let t = e.relativePath;
|
|
274
|
+
if (typeof t != "string") return e;
|
|
275
|
+
let n = t.replace(/^(\.?\/)+/, "");
|
|
276
|
+
return n.includes("/") && Object.defineProperty(e, "webkitRelativePath", {
|
|
277
|
+
value: n,
|
|
278
|
+
configurable: !0,
|
|
279
|
+
enumerable: !0
|
|
280
|
+
}), e;
|
|
281
|
+
}, U = async (e) => {
|
|
282
|
+
let t = [];
|
|
283
|
+
for (let n of e) n instanceof File ? t.push(H(n)) : "kind" in n && n.kind === "directory" && t.push(...await ee(n));
|
|
284
|
+
return t;
|
|
285
|
+
}, W = async (e, t) => {
|
|
286
|
+
let n = await U(e), i = V(n);
|
|
287
|
+
if (i.length > 1) throw C({
|
|
288
|
+
code: "multiple_models",
|
|
289
|
+
message: `Multiple models found: ${i.map((e) => e.name).join(", ")}`,
|
|
290
|
+
recoverable: !0,
|
|
291
|
+
source: "local-upload",
|
|
292
|
+
context: { fileNames: i.map((e) => e.name) }
|
|
293
|
+
});
|
|
294
|
+
if (i.length === 0) throw C({
|
|
295
|
+
code: "unsupported_format",
|
|
296
|
+
message: n.length ? `No supported model found in: ${n.map((e) => e.name).join(", ")}` : "No files to load",
|
|
297
|
+
recoverable: !0,
|
|
298
|
+
source: "local-upload",
|
|
299
|
+
context: { fileNames: n.map((e) => e.name) }
|
|
300
|
+
});
|
|
301
|
+
let a = i[0], o = p(a.name), s = o.isBundle ? n.filter((e) => e !== a) : [];
|
|
302
|
+
return o.isBundle && !r(o.id) ? K(a, s, t) : G(a, s, o.id, t);
|
|
303
|
+
}, G = async (e, t, n, { modelLoader: r, optimizer: i, publish: a }) => {
|
|
304
|
+
let o, s;
|
|
305
|
+
try {
|
|
306
|
+
let i = await r.loadToThreeJS(e, t);
|
|
307
|
+
s = i.glbBytes, o = { file: {
|
|
308
|
+
model: i.scene,
|
|
309
|
+
animations: i.animations,
|
|
310
|
+
type: n,
|
|
311
|
+
name: e.name
|
|
312
|
+
} };
|
|
313
|
+
} catch (t) {
|
|
314
|
+
throw T(t, "binary_load_failed", {
|
|
315
|
+
fileName: e.name,
|
|
316
|
+
fileType: n,
|
|
317
|
+
fileSize: e.size
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
return a(o), i && await D(async () => {
|
|
321
|
+
let t = s ?? new Uint8Array(await e.arrayBuffer());
|
|
322
|
+
await i.loadFromGlbBuffer(t);
|
|
323
|
+
}), o;
|
|
324
|
+
}, K = async (t, n, { modelLoader: r, optimizer: i, publish: o }) => {
|
|
325
|
+
let c;
|
|
326
|
+
try {
|
|
327
|
+
let { sourcePackageBytes: i, textureBytes: a } = await ie(t, n), o = await r.loadGLTFWithAssetsToThreeJS(t, n);
|
|
328
|
+
c = { file: {
|
|
329
|
+
model: o.scene,
|
|
330
|
+
animations: o.animations,
|
|
331
|
+
type: e.gltf,
|
|
332
|
+
name: t.name,
|
|
333
|
+
sourcePackageBytes: i,
|
|
334
|
+
sourceTextureBytes: a
|
|
335
|
+
} };
|
|
336
|
+
} catch (e) {
|
|
337
|
+
throw T(e, "gltf_load_failed", {
|
|
338
|
+
fileName: t.name,
|
|
339
|
+
fileSize: t.size,
|
|
340
|
+
assetCount: n.length
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
return o(c), i && await D(async () => {
|
|
344
|
+
try {
|
|
345
|
+
let e = await k(t, n);
|
|
346
|
+
await i.loadFromServerSceneData(e);
|
|
347
|
+
} catch (e) {
|
|
348
|
+
if (console.warn("Failed to initialize optimizer from source GLTF payload; trying direct asset load.", e), !("loadFromGLTFWithAssets" in i)) throw Error("loadFromGLTFWithAssets not available");
|
|
349
|
+
let r = new Uint8Array(await t.arrayBuffer()), o = /* @__PURE__ */ new Map();
|
|
350
|
+
for (let e of n) o.set(s(e), new Uint8Array(await e.arrayBuffer()));
|
|
351
|
+
let c = s(t), l = /* @__PURE__ */ new Map();
|
|
352
|
+
for (let e of O(JSON.parse(new TextDecoder().decode(r)))) {
|
|
353
|
+
let t = a(o, e, c);
|
|
354
|
+
t && l.set(e, t);
|
|
355
|
+
}
|
|
356
|
+
await i.loadFromGLTFWithAssets(r, l);
|
|
357
|
+
}
|
|
358
|
+
}, () => i.load(c.file.model)), c;
|
|
359
|
+
};
|
|
360
|
+
//#endregion
|
|
361
|
+
//#region packages/hooks/src/use-load-model/optimizer-integration.ts
|
|
362
|
+
function q(t, n, r, i) {
|
|
363
|
+
let a = c(async (a, o) => {
|
|
364
|
+
if (!t) {
|
|
365
|
+
console.warn("Optimizer is not available");
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
try {
|
|
369
|
+
a && await a(o);
|
|
370
|
+
let s = await t.getModel();
|
|
371
|
+
if (!s) {
|
|
372
|
+
console.warn("No optimized model available after optimization");
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
let c = s instanceof Uint8Array ? s.slice() : new Uint8Array(s).slice(), l = new File([c], r?.name || "optimized_model.glb", { type: "model/gltf-binary" }), u = await i.loadToThreeJS(l);
|
|
376
|
+
n({
|
|
377
|
+
model: u.scene,
|
|
378
|
+
animations: u.animations,
|
|
379
|
+
type: e.glb,
|
|
380
|
+
name: l.name
|
|
381
|
+
});
|
|
382
|
+
} catch (e) {
|
|
383
|
+
console.error("Optimization failed:", e);
|
|
384
|
+
}
|
|
385
|
+
}, [
|
|
386
|
+
t,
|
|
387
|
+
i,
|
|
388
|
+
r,
|
|
389
|
+
n
|
|
390
|
+
]);
|
|
391
|
+
return t ? {
|
|
392
|
+
...t,
|
|
393
|
+
isPreparing: !!r?.model && !t.isReady,
|
|
394
|
+
applyOptimization: a
|
|
395
|
+
} : null;
|
|
396
|
+
}
|
|
397
|
+
//#endregion
|
|
398
|
+
//#region packages/hooks/src/use-load-model/scene-loaders.ts
|
|
399
|
+
var oe = ({ sceneId: e, serverOptions: t }) => ({
|
|
400
|
+
endpoint: t?.endpoint ?? `/api/scenes/${e}`,
|
|
401
|
+
headers: t?.apiKey ? {
|
|
402
|
+
Authorization: `Bearer ${t.apiKey}`,
|
|
403
|
+
...t.headers
|
|
404
|
+
} : { ...t?.headers }
|
|
405
|
+
}), J = "__vctrl_published_model__", se = async (t, n, r, { modelLoader: i, publish: a, onProgress: o }, s) => {
|
|
406
|
+
let c = await B({
|
|
407
|
+
...n.assetRefs ?? {},
|
|
408
|
+
[J]: r
|
|
409
|
+
}, {
|
|
410
|
+
headers: s,
|
|
411
|
+
onProgress: (e) => o(Math.round(e * 60))
|
|
412
|
+
}), l = c[J];
|
|
413
|
+
if (delete c[J], !l) throw C({
|
|
414
|
+
code: "missing_assets",
|
|
415
|
+
message: "This scene has no published model to load.",
|
|
416
|
+
recoverable: !1,
|
|
417
|
+
source: "server-load",
|
|
418
|
+
context: { sceneId: t }
|
|
419
|
+
});
|
|
420
|
+
let u = z({
|
|
421
|
+
...n,
|
|
422
|
+
assetData: c
|
|
423
|
+
});
|
|
424
|
+
o(70);
|
|
425
|
+
let d = x(l), f = new Uint8Array(d.byteLength);
|
|
426
|
+
f.set(d);
|
|
427
|
+
let p = new Blob([f], { type: r.mimeType }), m = await i.loadToThreeJS(new File([p], r.fileName, { type: r.mimeType })), h = {
|
|
428
|
+
file: {
|
|
429
|
+
model: m.scene,
|
|
430
|
+
animations: m.animations,
|
|
431
|
+
type: e.glb,
|
|
432
|
+
name: u.meta?.name || r.fileName,
|
|
433
|
+
sourcePackageBytes: r.byteSize ?? void 0
|
|
434
|
+
},
|
|
435
|
+
sceneId: t,
|
|
436
|
+
sceneData: u
|
|
437
|
+
};
|
|
438
|
+
return a(h), h;
|
|
439
|
+
}, Y = async (t, n, r) => {
|
|
440
|
+
let { sceneId: i, sceneData: a, parseMode: o } = t, { modelLoader: s, optimizer: c, publish: l, onProgress: u } = n;
|
|
441
|
+
if (a.publishedModel) return se(i, a, a.publishedModel, n, r);
|
|
442
|
+
if (!a.gltfJson) throw C({
|
|
443
|
+
code: "missing_assets",
|
|
444
|
+
message: "This scene has no model data to load.",
|
|
445
|
+
recoverable: !1,
|
|
446
|
+
source: "server-load",
|
|
447
|
+
context: { sceneId: i }
|
|
448
|
+
});
|
|
449
|
+
let d = R(a.assetRefs && !a.assetData ? {
|
|
450
|
+
...a,
|
|
451
|
+
assetData: await B(a.assetRefs, {
|
|
452
|
+
headers: r,
|
|
453
|
+
onProgress: (e) => u(Math.round(e * 40))
|
|
454
|
+
})
|
|
455
|
+
} : a);
|
|
456
|
+
u(40);
|
|
457
|
+
let { sourcePackageBytes: f, textureBytes: p } = ae(d);
|
|
458
|
+
if (o === "direct" && d.gltfJson) {
|
|
459
|
+
let t = /* @__PURE__ */ new Map();
|
|
460
|
+
for (let e of Object.values(d.assetData ?? {})) e.fileName !== m && t.set(e.fileName, x(e));
|
|
461
|
+
u(60);
|
|
462
|
+
let n = await s.parseGLTFJsonToThreeJS(d.gltfJson, t), r = {
|
|
463
|
+
file: {
|
|
464
|
+
model: n.scene,
|
|
465
|
+
animations: n.animations,
|
|
466
|
+
type: e.gltf,
|
|
467
|
+
name: d.meta?.name || "scene",
|
|
468
|
+
sourcePackageBytes: f,
|
|
469
|
+
sourceTextureBytes: p
|
|
470
|
+
},
|
|
471
|
+
sceneId: i,
|
|
472
|
+
sceneData: d
|
|
473
|
+
};
|
|
474
|
+
return l(r), r;
|
|
475
|
+
}
|
|
476
|
+
let h = j(d);
|
|
477
|
+
u(60);
|
|
478
|
+
let g = h[0], _ = h.slice(1), v = await s.loadGLTFWithAssetsToThreeJS(g, _), y = {
|
|
479
|
+
file: {
|
|
480
|
+
model: v.scene,
|
|
481
|
+
animations: v.animations,
|
|
482
|
+
type: e.gltf,
|
|
483
|
+
name: g.name,
|
|
484
|
+
sourcePackageBytes: f,
|
|
485
|
+
sourceTextureBytes: p
|
|
486
|
+
},
|
|
487
|
+
sceneId: i,
|
|
488
|
+
sceneData: d
|
|
489
|
+
};
|
|
490
|
+
return l(y), c && await D(() => c.loadFromServerSceneData(d)), y;
|
|
491
|
+
}, ce = async (e, t) => {
|
|
492
|
+
let { sceneId: n, parseMode: r } = e, { onProgress: i } = t, { endpoint: a, headers: o } = oe(e);
|
|
493
|
+
try {
|
|
494
|
+
return i(0), await Y({
|
|
495
|
+
kind: "scene-data",
|
|
496
|
+
sceneId: n,
|
|
497
|
+
sceneData: await ue(a, o) ?? await de(a, o, n),
|
|
498
|
+
parseMode: r
|
|
499
|
+
}, t, o);
|
|
500
|
+
} catch (e) {
|
|
501
|
+
throw E(e, n);
|
|
502
|
+
}
|
|
503
|
+
}, le = /* @__PURE__ */ new Set([
|
|
504
|
+
401,
|
|
505
|
+
403,
|
|
506
|
+
404
|
|
507
|
+
]);
|
|
508
|
+
async function ue(e, t) {
|
|
509
|
+
try {
|
|
510
|
+
let n = await fetch(e, {
|
|
511
|
+
method: "GET",
|
|
512
|
+
headers: {
|
|
513
|
+
Accept: "application/json",
|
|
514
|
+
...t
|
|
515
|
+
}
|
|
516
|
+
});
|
|
517
|
+
if (le.has(n.status)) throw C({
|
|
518
|
+
code: n.status === 404 ? "not_found" : "server_load_failed",
|
|
519
|
+
message: `Server responded with ${n.status} ${n.statusText}`,
|
|
520
|
+
recoverable: !1,
|
|
521
|
+
source: "server-load",
|
|
522
|
+
context: {
|
|
523
|
+
endpoint: e,
|
|
524
|
+
status: n.status
|
|
525
|
+
}
|
|
526
|
+
});
|
|
527
|
+
if (!n.ok) return null;
|
|
528
|
+
let r = await n.json(), i = r.data ?? r;
|
|
529
|
+
return !i || typeof i != "object" ? null : i.publishedModel ? i : !i.gltfJson || !i.assetRefs && !i.assetData ? null : i;
|
|
530
|
+
} catch (e) {
|
|
531
|
+
if (S(e)) throw e;
|
|
532
|
+
return null;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
async function de(e, t, n) {
|
|
536
|
+
let r = new FormData();
|
|
537
|
+
r.append("action", "get-scene-settings"), r.append("sceneId", n);
|
|
538
|
+
let i = await fetch(e, {
|
|
539
|
+
method: "POST",
|
|
540
|
+
headers: t,
|
|
541
|
+
body: r
|
|
542
|
+
});
|
|
543
|
+
if (!i.ok) throw Error(`Server responded with ${i.status} ${i.statusText}`);
|
|
544
|
+
let a = await i.json();
|
|
545
|
+
return a.data ?? a;
|
|
546
|
+
}
|
|
547
|
+
//#endregion
|
|
548
|
+
//#region packages/hooks/src/use-load-model/state.ts
|
|
549
|
+
var X = [...f], Z = {
|
|
550
|
+
status: "empty",
|
|
551
|
+
file: null,
|
|
552
|
+
error: null,
|
|
553
|
+
source: null,
|
|
554
|
+
progress: 0
|
|
555
|
+
}, fe = (e, t = 0) => ({
|
|
556
|
+
status: "loading",
|
|
557
|
+
file: null,
|
|
558
|
+
error: null,
|
|
559
|
+
source: e,
|
|
560
|
+
progress: t
|
|
561
|
+
}), Q = (e, t) => ({
|
|
562
|
+
status: "ready",
|
|
563
|
+
file: t.file,
|
|
564
|
+
error: null,
|
|
565
|
+
source: e,
|
|
566
|
+
progress: 100,
|
|
567
|
+
sceneId: t.sceneId,
|
|
568
|
+
sceneData: t.sceneData
|
|
569
|
+
}), $ = (e, t) => ({
|
|
570
|
+
status: "error",
|
|
571
|
+
file: null,
|
|
572
|
+
error: t,
|
|
573
|
+
source: e,
|
|
574
|
+
progress: 0
|
|
575
|
+
});
|
|
576
|
+
//#endregion
|
|
577
|
+
//#region packages/hooks/src/use-load-model/use-load-model.ts
|
|
578
|
+
function pe(e) {
|
|
579
|
+
let [n, r] = d(Z), i = u(e);
|
|
580
|
+
i.current = e;
|
|
581
|
+
let a = u(0), o = l(() => {
|
|
582
|
+
let e = new t();
|
|
583
|
+
return e.onProgress((e) => {
|
|
584
|
+
r((t) => t.status === "loading" ? {
|
|
585
|
+
...t,
|
|
586
|
+
progress: e.progress
|
|
587
|
+
} : t);
|
|
588
|
+
}), e;
|
|
589
|
+
}, []), s = u(n);
|
|
590
|
+
s.current = n;
|
|
591
|
+
let f = u(null);
|
|
592
|
+
n.status === "ready" && (f.current = n);
|
|
593
|
+
let p = c(async (e, t) => {
|
|
594
|
+
let n = ++a.current, s = () => a.current === n, c = (e) => ({
|
|
595
|
+
...e,
|
|
596
|
+
stillCurrent: s
|
|
597
|
+
}), l = e.kind === "files" ? f.current : null, u = (e) => (s() && (e.status === "ready" && (f.current = e), r(e)), e), d = null, p = {
|
|
598
|
+
modelLoader: o,
|
|
599
|
+
optimizer: i.current,
|
|
600
|
+
publish: (n) => {
|
|
601
|
+
d = u(Q(e.kind, n)), s() && t?.onPublish?.(c(d));
|
|
602
|
+
},
|
|
603
|
+
onProgress: (e) => {
|
|
604
|
+
s() && r((t) => t.status === "loading" ? {
|
|
605
|
+
...t,
|
|
606
|
+
progress: e
|
|
607
|
+
} : t);
|
|
608
|
+
}
|
|
609
|
+
};
|
|
610
|
+
u(fe(e.kind));
|
|
611
|
+
try {
|
|
612
|
+
let t = e.kind === "files" ? await W(e.files, p) : e.kind === "scene-data" ? await Y(e, p) : await ce(e, p);
|
|
613
|
+
return c(d ?? u(Q(e.kind, t)));
|
|
614
|
+
} catch (t) {
|
|
615
|
+
let n = e.kind === "files" ? T(t, "unknown") : E(t, e.sceneId ?? "");
|
|
616
|
+
return console.error(`Model load failed (${e.kind}):`, n), u(l ?? $(e.kind, n)), c($(e.kind, n));
|
|
617
|
+
}
|
|
618
|
+
}, [o]), m = c(() => {
|
|
619
|
+
a.current += 1, f.current = null, r(Z);
|
|
620
|
+
}, []), h = q(e, c((e) => {
|
|
621
|
+
r((t) => t.status === "ready" ? {
|
|
622
|
+
...t,
|
|
623
|
+
file: e
|
|
624
|
+
} : t);
|
|
625
|
+
}, []), n.file, o);
|
|
626
|
+
return {
|
|
627
|
+
...n,
|
|
628
|
+
supportedFileTypes: X,
|
|
629
|
+
load: p,
|
|
630
|
+
reset: m,
|
|
631
|
+
optimizer: h
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
//#endregion
|
|
635
|
+
export { B as n, j as r, pe as t };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
let e=require("@vctrl/core/model-loader"),t=require("react"),n=require("@vctrl/core/model-formats"),r=require("@vctrl/core");var i=e=>typeof e==`object`&&!!e&&`code`in e&&`recoverable`in e&&`source`in e,a=({code:e,message:t,recoverable:n,source:r,cause:i,context:a})=>({code:e,message:t,recoverable:n,source:r,cause:i,context:a}),o=e=>e instanceof Error&&e.message?e.message:typeof e==`string`&&e.trim().length>0?e:`Unknown error`,s=(t,n,r)=>i(t)?t:a({code:(0,e.isMissingAssetsError)(t)?`missing_assets`:n,message:o(t),recoverable:!0,source:`local-upload`,cause:t,context:r}),c=(t,n)=>{if(i(t))return t;let r=o(t),s=(0,e.isMissingAssetsError)(t)?`missing_assets`:`server_load_failed`;return r.includes(`Server responded with 404`)?s=`not_found`:(r.includes(`Server responded with 402`)||r.includes(`Server responded with 403`))&&(s=`quota_exceeded`),a({code:s,message:r,recoverable:s!==`not_found`,source:`server-load`,cause:t,context:{sceneId:n}})},l=async(e,t)=>{try{await e();return}catch(e){console.warn(`Optimizer ingest failed.`,e)}if(t)try{await t()}catch(e){console.warn(`Optimizer fallback import failed; optimization is unavailable for this model.`,e)}},u=e=>{let t=new Set,n=e,r=Array.isArray(n.images)?n.images:[];for(let e of r)typeof e.uri==`string`&&!e.uri.startsWith(`data:`)&&t.add(e.uri);let i=Array.isArray(n.buffers)?n.buffers:[];for(let e of i)typeof e.uri==`string`&&!e.uri.startsWith(`data:`)&&t.add(e.uri);return t},d=async(t,n)=>{let i=JSON.parse(await t.text()),a=u(i),o={},s=new Map,c=new Map,l=new Set,d=async e=>{let t=c.get(e);if(t)return t;let n=new Uint8Array(await e.arrayBuffer());return c.set(e,n),n};for(let t of n)s.set((0,e.selectionKey)(t),t);let f=(0,e.selectionKey)(t),p=[];for(let t of a){let n=(0,r.normalizeAssetUri)(t),i=(0,e.referenceIn)(s,t,f);if(!i){p.push(t);continue}l.add(i);let a=await d(i);o[n]={data:Array.from(a),fileName:n,mimeType:i.type||`application/octet-stream`}}if(p.length>0)throw(0,e.missingAssetsError)(`Scene payload is missing required referenced assets: ${p.slice(0,5).join(`, `)}`);for(let[e,t]of n.entries()){if(l.has(t))continue;let n=(0,r.normalizeAssetUri)(t.name),i=n.split(`/`).pop()||n,a=await d(t);o[`extra-${e}-${i}`]={data:Array.from(a),fileName:i,mimeType:t.type||`application/octet-stream`}}return{gltfJson:i,assetData:o}};async function f(e){let t=[];async function*n(e,t){let r=e;for await(let[e,i]of r)i.kind===`file`?yield p(await i.getFile(),`${t}${e}`):i.kind===`directory`&&(yield*n(i,`${t}${e}/`))}for await(let r of n(e,`${e.name}/`))t.push(r);return t}function p(e,t){return Object.defineProperty(e,"webkitRelativePath",{value:t,configurable:!0,enumerable:!0}),e}function m(e){let t=(e??``).replace(/[\\/]+/g,`_`).replace(/^\.+/,``);return t?`${t}.gltf`:`scene.gltf`}function h(e){let t=[];if(e.assetData&&typeof e.assetData==`object`)for(let[,n]of Object.entries(e.assetData)){let{fileName:e,mimeType:i}=n;if(e===r.PERSISTED_BAKE_FILENAME)continue;let a=(0,r.toSerializedAssetBytes)(n),o=new Uint8Array(a.byteLength);o.set(a);let s=new Blob([o],{type:i}),c=new File([s],e,{type:i});t.push(c)}if(!e.gltfJson)throw Error(`Cannot reconstruct glTF files: this scene has no glTF document.`);let n=JSON.stringify(e.gltfJson),i=new Blob([n],{type:`model/gltf+json`}),a=m(e.meta?.name);return[new File([i],a,{type:`model/gltf+json`}),...t]}var g=t=>{let n=new Map;for(let i of Object.values(t.assetData||{}))n.set((0,e.resolutionKey)(i.fileName),(0,r.getSerializedAssetByteSize)(i.data));return t=>(0,e.referenceIn)(n,t)??0},_=(t,n)=>{let r=new Map;for(let t of n)r.set((0,e.selectionKey)(t),t.size);let i=(0,e.selectionKey)(t);return t=>(0,e.referenceIn)(r,t,i)??0},v=e=>{let t=new Set,n=new Set,r=e,i=Array.isArray(r.images)?r.images:[];for(let e of i)typeof e.uri==`string`&&!e.uri.startsWith(`data:`)&&n.add(e.uri);let a=Array.isArray(r.buffers)?r.buffers:[];for(let e of a)typeof e.uri==`string`&&!e.uri.startsWith(`data:`)&&t.add(e.uri);return{bufferUris:t,imageUris:n}};async function y(e,t){let n=await e.text(),r;try{r=JSON.parse(n)}catch{return{sourcePackageBytes:e.size,textureBytes:0}}let i=_(e,t),{bufferUris:a,imageUris:o}=v(r),s=Array.from(a).reduce((e,t)=>e+i(t),0),c=Array.from(o).reduce((e,t)=>e+i(t),0);return{sourcePackageBytes:e.size+s+c,textureBytes:c}}function b(e){let t=e.gltfJson;if(!t)return{sourcePackageBytes:0,textureBytes:0};let n=g(e),r=new TextEncoder().encode(JSON.stringify(t)).byteLength,{bufferUris:i,imageUris:a}=v(t),o=Array.from(i).reduce((e,t)=>e+n(t),0),s=Array.from(a).reduce((e,t)=>e+n(t),0);return{sourcePackageBytes:r+o+s,textureBytes:s}}function x(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function S(e){let t=x(e.settings)?e.settings:{},n=(0,r.normalizeCameraSettings)(e.camera??t.camera);return{...t,bounds:e.bounds??t.bounds,camera:n,interactions:(0,r.normalizeSceneInteractions)(t.interactions,{camera:n}),controls:e.controls??t.controls,environment:e.environment??t.environment,shadows:e.shadows??t.shadows}}function C(e){let t=new Set,n=e,r=Array.isArray(n.images)?n.images:[];for(let e of r)typeof e.uri==`string`&&!e.uri.startsWith(`data:`)&&t.add(e.uri);let i=Array.isArray(n.buffers)?n.buffers:[];for(let e of i)typeof e.uri==`string`&&!e.uri.startsWith(`data:`)&&t.add(e.uri);return t}function w(e){for(let[t,n]of Object.entries(e)){if(!n||typeof n!=`object`)throw Error(`Scene payload contains invalid asset entry for ${t}`);if(typeof n.fileName!=`string`||n.fileName.trim().length===0)throw Error(`Scene payload asset ${t} is missing a valid fileName`);if(typeof n.mimeType!=`string`||n.mimeType.trim().length===0)throw Error(`Scene payload asset ${t} is missing a valid mimeType`);if(typeof n.data==`string`&&n.encoding===`base64`&&!(0,r.isValidBase64)(n.data))throw Error(`Scene payload asset ${n.fileName} has invalid base64 data`)}}function T(t,n){let i=C(t);if(i.size===0)return;let a=new Set;for(let e of Object.values(n))for(let t of(0,r.buildAssetLookupKeys)(e.fileName))a.add(t);let o=[];for(let e of i){let t=(0,r.normalizeAssetUri)(e),n=t.split(`/`).pop()||t;!a.has(e)&&!a.has(t)&&!a.has(n)&&o.push(e)}if(o.length>0)throw(0,e.missingAssetsError)(`Scene payload is missing required referenced assets: ${o.slice(0,5).join(`, `)}`)}function E(e){if(!x(e.gltfJson))throw Error(`Scene payload is missing a valid glTF document`);let t=e.assetData??{};return w(t),T(e.gltfJson,t),{meta:e.meta,gltfJson:e.gltfJson,assetData:t,...S(e)}}function D(e){let t=e.assetData??{};return w(t),{meta:e.meta,gltfJson:null,assetData:t,...S(e)}}async function O(e,t={}){let n=Object.entries(e);if(n.length===0)return t.onProgress?.(1),{};let r=n.every(([,e])=>e.byteSize!=null),i=r?n.reduce((e,[,t])=>e+(t.byteSize??0),0):0,a=0,o=0,s={},c=new AbortController;return await Promise.all(n.map(async([e,l])=>{try{let u=await fetch(l.url,{headers:t.headers,credentials:`same-origin`,signal:c.signal});if(!u.ok)throw c.abort(),Error(`Failed to fetch scene asset ${l.fileName}: ${u.status}`);let d=new Uint8Array(await u.arrayBuffer());s[e]={data:d,fileName:l.fileName,mimeType:l.mimeType},o+=1,a+=l.byteSize??0,t.onProgress?.(r&&i>0?a/i:o/n.length)}catch(e){throw c.abort(),e}})),s}var k=e=>{let t=new Map;for(let r of e){let e=(0,n.modelFormatForFileName)(r.name);!e?.canImport||t.has(e.id)||t.set(e.id,r)}return n.IMPORTABLE_FORMAT_IDS.map(e=>t.get(e)).filter(e=>!!e)},A=e=>{if(e.webkitRelativePath)return e;let t=e.relativePath;if(typeof t!=`string`)return e;let n=t.replace(/^(\.?\/)+/,``);return n.includes(`/`)&&Object.defineProperty(e,"webkitRelativePath",{value:n,configurable:!0,enumerable:!0}),e},j=async e=>{let t=[];for(let n of e)n instanceof File?t.push(A(n)):`kind`in n&&n.kind===`directory`&&t.push(...await f(n));return t},M=async(t,r)=>{let i=await j(t),o=k(i);if(o.length>1)throw a({code:`multiple_models`,message:`Multiple models found: ${o.map(e=>e.name).join(`, `)}`,recoverable:!0,source:`local-upload`,context:{fileNames:o.map(e=>e.name)}});if(o.length===0)throw a({code:`unsupported_format`,message:i.length?`No supported model found in: ${i.map(e=>e.name).join(`, `)}`:`No files to load`,recoverable:!0,source:`local-upload`,context:{fileNames:i.map(e=>e.name)}});let s=o[0],c=(0,n.modelFormatForFileName)(s.name),l=c.isBundle?i.filter(e=>e!==s):[];return c.isBundle&&!(0,e.isThreeSourceFormat)(c.id)?P(s,l,r):N(s,l,c.id,r)},N=async(e,t,n,{modelLoader:r,optimizer:i,publish:a})=>{let o,c;try{let i=await r.loadToThreeJS(e,t);c=i.glbBytes,o={file:{model:i.scene,animations:i.animations,type:n,name:e.name}}}catch(t){throw s(t,`binary_load_failed`,{fileName:e.name,fileType:n,fileSize:e.size})}return a(o),i&&await l(async()=>{let t=c??new Uint8Array(await e.arrayBuffer());await i.loadFromGlbBuffer(t)}),o},P=async(t,n,{modelLoader:r,optimizer:i,publish:a})=>{let o;try{let{sourcePackageBytes:i,textureBytes:a}=await y(t,n),s=await r.loadGLTFWithAssetsToThreeJS(t,n);o={file:{model:s.scene,animations:s.animations,type:e.ModelFileTypes.gltf,name:t.name,sourcePackageBytes:i,sourceTextureBytes:a}}}catch(e){throw s(e,`gltf_load_failed`,{fileName:t.name,fileSize:t.size,assetCount:n.length})}return a(o),i&&await l(async()=>{try{let e=await d(t,n);await i.loadFromServerSceneData(e)}catch(r){if(console.warn(`Failed to initialize optimizer from source GLTF payload; trying direct asset load.`,r),!(`loadFromGLTFWithAssets`in i))throw Error(`loadFromGLTFWithAssets not available`);let a=new Uint8Array(await t.arrayBuffer()),o=new Map;for(let t of n)o.set((0,e.selectionKey)(t),new Uint8Array(await t.arrayBuffer()));let s=(0,e.selectionKey)(t),c=new Map;for(let t of u(JSON.parse(new TextDecoder().decode(a)))){let n=(0,e.referenceIn)(o,t,s);n&&c.set(t,n)}await i.loadFromGLTFWithAssets(a,c)}},()=>i.load(o.file.model)),o};function F(n,r,i,a){let o=(0,t.useCallback)(async(t,o)=>{if(!n){console.warn(`Optimizer is not available`);return}try{t&&await t(o);let s=await n.getModel();if(!s){console.warn(`No optimized model available after optimization`);return}let c=s instanceof Uint8Array?s.slice():new Uint8Array(s).slice(),l=new File([c],i?.name||`optimized_model.glb`,{type:`model/gltf-binary`}),u=await a.loadToThreeJS(l);r({model:u.scene,animations:u.animations,type:e.ModelFileTypes.glb,name:l.name})}catch(e){console.error(`Optimization failed:`,e)}},[n,a,i,r]);return n?{...n,isPreparing:!!i?.model&&!n.isReady,applyOptimization:o}:null}var I=({sceneId:e,serverOptions:t})=>({endpoint:t?.endpoint??`/api/scenes/${e}`,headers:t?.apiKey?{Authorization:`Bearer ${t.apiKey}`,...t.headers}:{...t?.headers}}),L=`__vctrl_published_model__`,R=async(t,n,i,{modelLoader:o,publish:s,onProgress:c},l)=>{let u=await O({...n.assetRefs??{},[L]:i},{headers:l,onProgress:e=>c(Math.round(e*60))}),d=u[L];if(delete u[L],!d)throw a({code:`missing_assets`,message:`This scene has no published model to load.`,recoverable:!1,source:`server-load`,context:{sceneId:t}});let f=D({...n,assetData:u});c(70);let p=(0,r.toSerializedAssetBytes)(d),m=new Uint8Array(p.byteLength);m.set(p);let h=new Blob([m],{type:i.mimeType}),g=await o.loadToThreeJS(new File([h],i.fileName,{type:i.mimeType})),_={file:{model:g.scene,animations:g.animations,type:e.ModelFileTypes.glb,name:f.meta?.name||i.fileName,sourcePackageBytes:i.byteSize??void 0},sceneId:t,sceneData:f};return s(_),_},z=async(t,n,i)=>{let{sceneId:o,sceneData:s,parseMode:c}=t,{modelLoader:u,optimizer:d,publish:f,onProgress:p}=n;if(s.publishedModel)return R(o,s,s.publishedModel,n,i);if(!s.gltfJson)throw a({code:`missing_assets`,message:`This scene has no model data to load.`,recoverable:!1,source:`server-load`,context:{sceneId:o}});let m=E(s.assetRefs&&!s.assetData?{...s,assetData:await O(s.assetRefs,{headers:i,onProgress:e=>p(Math.round(e*40))})}:s);p(40);let{sourcePackageBytes:g,textureBytes:_}=b(m);if(c===`direct`&&m.gltfJson){let t=new Map;for(let e of Object.values(m.assetData??{}))e.fileName!==r.PERSISTED_BAKE_FILENAME&&t.set(e.fileName,(0,r.toSerializedAssetBytes)(e));p(60);let n=await u.parseGLTFJsonToThreeJS(m.gltfJson,t),i={file:{model:n.scene,animations:n.animations,type:e.ModelFileTypes.gltf,name:m.meta?.name||`scene`,sourcePackageBytes:g,sourceTextureBytes:_},sceneId:o,sceneData:m};return f(i),i}let v=h(m);p(60);let y=v[0],x=v.slice(1),S=await u.loadGLTFWithAssetsToThreeJS(y,x),C={file:{model:S.scene,animations:S.animations,type:e.ModelFileTypes.gltf,name:y.name,sourcePackageBytes:g,sourceTextureBytes:_},sceneId:o,sceneData:m};return f(C),d&&await l(()=>d.loadFromServerSceneData(m)),C},B=async(e,t)=>{let{sceneId:n,parseMode:r}=e,{onProgress:i}=t,{endpoint:a,headers:o}=I(e);try{return i(0),await z({kind:`scene-data`,sceneId:n,sceneData:await H(a,o)??await U(a,o,n),parseMode:r},t,o)}catch(e){throw c(e,n)}},V=new Set([401,403,404]);async function H(e,t){try{let n=await fetch(e,{method:`GET`,headers:{Accept:`application/json`,...t}});if(V.has(n.status))throw a({code:n.status===404?`not_found`:`server_load_failed`,message:`Server responded with ${n.status} ${n.statusText}`,recoverable:!1,source:`server-load`,context:{endpoint:e,status:n.status}});if(!n.ok)return null;let r=await n.json(),i=r.data??r;return!i||typeof i!=`object`?null:i.publishedModel?i:!i.gltfJson||!i.assetRefs&&!i.assetData?null:i}catch(e){if(i(e))throw e;return null}}async function U(e,t,n){let r=new FormData;r.append(`action`,`get-scene-settings`),r.append(`sceneId`,n);let i=await fetch(e,{method:`POST`,headers:t,body:r});if(!i.ok)throw Error(`Server responded with ${i.status} ${i.statusText}`);let a=await i.json();return a.data??a}var W=[...n.IMPORTABLE_FORMAT_IDS],G={status:`empty`,file:null,error:null,source:null,progress:0},K=(e,t=0)=>({status:`loading`,file:null,error:null,source:e,progress:t}),q=(e,t)=>({status:`ready`,file:t.file,error:null,source:e,progress:100,sceneId:t.sceneId,sceneData:t.sceneData}),J=(e,t)=>({status:`error`,file:null,error:t,source:e,progress:0});function Y(n){let[r,i]=(0,t.useState)(G),a=(0,t.useRef)(n);a.current=n;let o=(0,t.useRef)(0),l=(0,t.useMemo)(()=>{let t=new e.ModelLoader;return t.onProgress(e=>{i(t=>t.status===`loading`?{...t,progress:e.progress}:t)}),t},[]),u=(0,t.useRef)(r);u.current=r;let d=(0,t.useRef)(null);r.status===`ready`&&(d.current=r);let f=(0,t.useCallback)(async(e,t)=>{let n=++o.current,r=()=>o.current===n,u=e=>({...e,stillCurrent:r}),f=e.kind===`files`?d.current:null,p=e=>(r()&&(e.status===`ready`&&(d.current=e),i(e)),e),m=null,h={modelLoader:l,optimizer:a.current,publish:n=>{m=p(q(e.kind,n)),r()&&t?.onPublish?.(u(m))},onProgress:e=>{r()&&i(t=>t.status===`loading`?{...t,progress:e}:t)}};p(K(e.kind));try{let t=e.kind===`files`?await M(e.files,h):e.kind===`scene-data`?await z(e,h):await B(e,h);return u(m??p(q(e.kind,t)))}catch(t){let n=e.kind===`files`?s(t,`unknown`):c(t,e.sceneId??``);return console.error(`Model load failed (${e.kind}):`,n),p(f??J(e.kind,n)),u(J(e.kind,n))}},[l]),p=(0,t.useCallback)(()=>{o.current+=1,d.current=null,i(G)},[]),m=F(n,(0,t.useCallback)(e=>{i(t=>t.status===`ready`?{...t,file:e}:t)},[]),r.file,l);return{...r,supportedFileTypes:W,load:f,reset:p,optimizer:m}}Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return O}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return h}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return Y}});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./rolldown-runtime-BocRIvOZ.cjs"),t=require("./use-load-model-CZCi6bPM.cjs");let n=require("react");n=e.t(n,1);let r=require("react/jsx-runtime");var i=(0,n.createContext)(void 0),a=(0,n.createContext)(void 0),o=({children:e,optimizer:n})=>{let o=t.t(n);return n?(0,r.jsx)(i.Provider,{value:o,children:e}):(0,r.jsx)(a.Provider,{value:o,children:e})};function s(e){let t=(0,n.useContext)(i),r=(0,n.useContext)(a),o=t??r;if(!o)throw Error(`useModelContext must be used within a ModelProvider`);return o}exports.ModelProvider=o,exports.fetchManifestAssetData=t.n,exports.reconstructGltfFiles=t.r,exports.useLoadModel=t.t,exports.useModelContext=s;
|
package/use-load-model.es.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as e, r as t, t as n } from "./use-load-model-
|
|
1
|
+
import { n as e, r as t, t as n } from "./use-load-model-CRDVzrPC.js";
|
|
2
2
|
import { createContext as r, useContext as i } from "react";
|
|
3
3
|
import { jsx as a } from "react/jsx-runtime";
|
|
4
4
|
//#region packages/hooks/src/use-load-model/model-context.tsx
|
|
@@ -13,10 +13,12 @@ export interface OptimizationInfo {
|
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
15
15
|
* Interface representing the state of the model optimizer.
|
|
16
|
+
*
|
|
17
|
+
* `OptimizationInfo` is not part of it: it is derived from `report` on every
|
|
18
|
+
* render by `useCalcOptimizationInfo`, never stored.
|
|
16
19
|
*/
|
|
17
20
|
export interface OptimizationState {
|
|
18
21
|
report: OptimizationReport | null;
|
|
19
|
-
info: OptimizationInfo | null;
|
|
20
22
|
error: Error | null;
|
|
21
23
|
loading: boolean;
|
|
22
24
|
}
|