@plasius/gpu-shared 0.1.16 → 0.1.17
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 +6 -0
- package/README.md +3 -0
- package/dist/{chunk-6SOHFUOE.js → chunk-BXTHIQOO.js} +41 -3
- package/dist/chunk-BXTHIQOO.js.map +1 -0
- package/dist/chunk-UKCJ2AWJ.js +793 -0
- package/dist/chunk-UKCJ2AWJ.js.map +1 -0
- package/dist/{gltf-loader-B6VOWGBV.js → gltf-loader-FMRC3OEV.js} +2 -2
- package/dist/index.cjs +401 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +5 -5
- package/dist/{product-studio-runtime-BYVBUWIN.js → product-studio-runtime-CTXA45RJ.js} +3 -3
- package/dist/{showcase-runtime-M6TEUYOG.js → showcase-runtime-OH3H6ZW2.js} +2 -2
- package/package.json +1 -1
- package/src/gltf-loader.js +447 -15
- package/src/index.d.ts +83 -0
- package/src/product-studio-runtime.js +53 -0
- package/dist/chunk-6SOHFUOE.js.map +0 -1
- package/dist/chunk-QVNRTWHB.js +0 -445
- package/dist/chunk-QVNRTWHB.js.map +0 -1
- /package/dist/{gltf-loader-B6VOWGBV.js.map → gltf-loader-FMRC3OEV.js.map} +0 -0
- /package/dist/{product-studio-runtime-BYVBUWIN.js.map → product-studio-runtime-CTXA45RJ.js.map} +0 -0
- /package/dist/{showcase-runtime-M6TEUYOG.js.map → showcase-runtime-OH3H6ZW2.js.map} +0 -0
|
@@ -0,0 +1,793 @@
|
|
|
1
|
+
import {
|
|
2
|
+
INLINE_SHOWCASE_ASSET_URLS
|
|
3
|
+
} from "./chunk-2GM64LB6.js";
|
|
4
|
+
|
|
5
|
+
// src/asset-url.js
|
|
6
|
+
var SHOWCASE_ASSET_FILES = Object.freeze({
|
|
7
|
+
brigantine: "brigantine.gltf",
|
|
8
|
+
cutter: "cutter.gltf",
|
|
9
|
+
lighthouse: "lighthouse.gltf",
|
|
10
|
+
"harbor-dock": "harbor-dock.gltf"
|
|
11
|
+
});
|
|
12
|
+
function createInlineShowcaseAssetUrl(assetName) {
|
|
13
|
+
const inlineUrl = INLINE_SHOWCASE_ASSET_URLS[assetName];
|
|
14
|
+
return inlineUrl ? new URL(inlineUrl) : null;
|
|
15
|
+
}
|
|
16
|
+
function getBrowserBaseUrl() {
|
|
17
|
+
if (typeof document !== "undefined" && typeof document.baseURI === "string" && document.baseURI.length > 0) {
|
|
18
|
+
return document.baseURI;
|
|
19
|
+
}
|
|
20
|
+
if (typeof window !== "undefined" && typeof window.location?.href === "string" && window.location.href.length > 0) {
|
|
21
|
+
return window.location.href;
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
function normalizeAssetName(assetName) {
|
|
26
|
+
return typeof assetName === "string" && assetName in SHOWCASE_ASSET_FILES ? assetName : "brigantine";
|
|
27
|
+
}
|
|
28
|
+
function parseResolveArgs(baseUrlOrAssetName, assetName) {
|
|
29
|
+
if (typeof baseUrlOrAssetName === "string" && baseUrlOrAssetName in SHOWCASE_ASSET_FILES && typeof assetName === "undefined") {
|
|
30
|
+
return {
|
|
31
|
+
baseUrl: import.meta.url,
|
|
32
|
+
assetName: baseUrlOrAssetName
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
baseUrl: baseUrlOrAssetName ?? import.meta.url,
|
|
37
|
+
assetName: normalizeAssetName(assetName)
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function resolveShowcaseAssetUrl(baseUrlOrAssetName, assetName) {
|
|
41
|
+
const resolved = parseResolveArgs(baseUrlOrAssetName, assetName);
|
|
42
|
+
const fileName = SHOWCASE_ASSET_FILES[resolved.assetName];
|
|
43
|
+
try {
|
|
44
|
+
return new URL(`../assets/${fileName}`, resolved.baseUrl);
|
|
45
|
+
} catch {
|
|
46
|
+
const browserBaseUrl = getBrowserBaseUrl();
|
|
47
|
+
if (browserBaseUrl) {
|
|
48
|
+
try {
|
|
49
|
+
const normalizedBaseUrl = new URL(resolved.baseUrl, browserBaseUrl);
|
|
50
|
+
return new URL(`../assets/${fileName}`, normalizedBaseUrl);
|
|
51
|
+
} catch {
|
|
52
|
+
const inlineAsset = createInlineShowcaseAssetUrl(resolved.assetName);
|
|
53
|
+
if (inlineAsset) {
|
|
54
|
+
return inlineAsset;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
return new URL(`../assets/${fileName}`, import.meta.url);
|
|
60
|
+
} catch {
|
|
61
|
+
return new URL(`assets/${fileName}`, "file:///");
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function shouldUseInlineShowcaseFallback(url) {
|
|
66
|
+
const href = url instanceof URL ? url.href : String(url ?? "");
|
|
67
|
+
return href.includes("/assets/");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// src/gltf-loader.js
|
|
71
|
+
function decodeDataUri(uri) {
|
|
72
|
+
const match = /^data:.*?;base64,(.+)$/i.exec(uri);
|
|
73
|
+
if (!match) {
|
|
74
|
+
throw new Error(`Unsupported glTF buffer URI: ${uri.slice(0, 48)}`);
|
|
75
|
+
}
|
|
76
|
+
const binary = atob(match[1]);
|
|
77
|
+
const bytes = new Uint8Array(binary.length);
|
|
78
|
+
for (let index = 0; index < binary.length; index += 1) {
|
|
79
|
+
bytes[index] = binary.charCodeAt(index);
|
|
80
|
+
}
|
|
81
|
+
return bytes.buffer;
|
|
82
|
+
}
|
|
83
|
+
function getComponentArray(componentType, buffer, byteOffset, count) {
|
|
84
|
+
switch (componentType) {
|
|
85
|
+
case 5121:
|
|
86
|
+
return new Uint8Array(buffer, byteOffset, count);
|
|
87
|
+
case 5123:
|
|
88
|
+
return new Uint16Array(buffer, byteOffset, count);
|
|
89
|
+
case 5125:
|
|
90
|
+
return new Uint32Array(buffer, byteOffset, count);
|
|
91
|
+
case 5126:
|
|
92
|
+
return new Float32Array(buffer, byteOffset, count);
|
|
93
|
+
default:
|
|
94
|
+
throw new Error(`Unsupported glTF componentType: ${componentType}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function getNormalizationScale(componentType) {
|
|
98
|
+
switch (componentType) {
|
|
99
|
+
case 5121:
|
|
100
|
+
return 255;
|
|
101
|
+
case 5123:
|
|
102
|
+
return 65535;
|
|
103
|
+
default:
|
|
104
|
+
return 1;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function getTypeSize(type) {
|
|
108
|
+
switch (type) {
|
|
109
|
+
case "SCALAR":
|
|
110
|
+
return 1;
|
|
111
|
+
case "VEC2":
|
|
112
|
+
return 2;
|
|
113
|
+
case "VEC3":
|
|
114
|
+
return 3;
|
|
115
|
+
case "VEC4":
|
|
116
|
+
return 4;
|
|
117
|
+
default:
|
|
118
|
+
throw new Error(`Unsupported glTF accessor type: ${type}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function getComponentByteSize(componentType) {
|
|
122
|
+
switch (componentType) {
|
|
123
|
+
case 5121:
|
|
124
|
+
return 1;
|
|
125
|
+
case 5123:
|
|
126
|
+
return 2;
|
|
127
|
+
case 5125:
|
|
128
|
+
case 5126:
|
|
129
|
+
return 4;
|
|
130
|
+
default:
|
|
131
|
+
throw new Error(`Unsupported glTF componentType: ${componentType}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
function readComponentValue(view, componentType, byteOffset) {
|
|
135
|
+
switch (componentType) {
|
|
136
|
+
case 5121:
|
|
137
|
+
return view.getUint8(byteOffset);
|
|
138
|
+
case 5123:
|
|
139
|
+
return view.getUint16(byteOffset, true);
|
|
140
|
+
case 5125:
|
|
141
|
+
return view.getUint32(byteOffset, true);
|
|
142
|
+
case 5126:
|
|
143
|
+
return view.getFloat32(byteOffset, true);
|
|
144
|
+
default:
|
|
145
|
+
throw new Error(`Unsupported glTF componentType: ${componentType}`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
function readAccessor(document2, accessorIndex, buffers) {
|
|
149
|
+
const accessor = document2.accessors?.[accessorIndex];
|
|
150
|
+
if (!accessor) {
|
|
151
|
+
throw new Error(`glTF accessor ${accessorIndex} is missing.`);
|
|
152
|
+
}
|
|
153
|
+
const bufferView = document2.bufferViews?.[accessor.bufferView];
|
|
154
|
+
if (!bufferView) {
|
|
155
|
+
throw new Error(`glTF bufferView ${accessor.bufferView} is missing.`);
|
|
156
|
+
}
|
|
157
|
+
const buffer = buffers[bufferView.buffer];
|
|
158
|
+
const componentCount = getTypeSize(accessor.type);
|
|
159
|
+
const byteOffset = (bufferView.byteOffset ?? 0) + (accessor.byteOffset ?? 0);
|
|
160
|
+
const componentByteSize = getComponentByteSize(accessor.componentType);
|
|
161
|
+
const packedElementByteLength = componentCount * componentByteSize;
|
|
162
|
+
const byteStride = Math.max(bufferView.byteStride ?? packedElementByteLength, packedElementByteLength);
|
|
163
|
+
let values;
|
|
164
|
+
if (byteStride === packedElementByteLength) {
|
|
165
|
+
const valueCount = accessor.count * componentCount;
|
|
166
|
+
values = Array.from(
|
|
167
|
+
getComponentArray(accessor.componentType, buffer, byteOffset, valueCount)
|
|
168
|
+
);
|
|
169
|
+
} else {
|
|
170
|
+
const view = new DataView(buffer, byteOffset);
|
|
171
|
+
values = new Array(accessor.count * componentCount);
|
|
172
|
+
for (let index = 0; index < accessor.count; index += 1) {
|
|
173
|
+
const elementOffset = index * byteStride;
|
|
174
|
+
for (let componentIndex = 0; componentIndex < componentCount; componentIndex += 1) {
|
|
175
|
+
values[index * componentCount + componentIndex] = readComponentValue(
|
|
176
|
+
view,
|
|
177
|
+
accessor.componentType,
|
|
178
|
+
elementOffset + componentIndex * componentByteSize
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (accessor.normalized) {
|
|
184
|
+
const scale = getNormalizationScale(accessor.componentType);
|
|
185
|
+
return values.map((value) => value / scale);
|
|
186
|
+
}
|
|
187
|
+
return values;
|
|
188
|
+
}
|
|
189
|
+
async function decodeImagePixels(blob, urlLabel = "glTF texture") {
|
|
190
|
+
if (typeof createImageBitmap === "function") {
|
|
191
|
+
const bitmap = await createImageBitmap(blob);
|
|
192
|
+
try {
|
|
193
|
+
const canvas = typeof OffscreenCanvas === "function" ? new OffscreenCanvas(bitmap.width, bitmap.height) : typeof document !== "undefined" ? Object.assign(document.createElement("canvas"), {
|
|
194
|
+
width: bitmap.width,
|
|
195
|
+
height: bitmap.height
|
|
196
|
+
}) : null;
|
|
197
|
+
const context = canvas?.getContext?.("2d", { willReadFrequently: true });
|
|
198
|
+
if (!context) {
|
|
199
|
+
throw new Error("Unable to create 2D context for glTF texture decode.");
|
|
200
|
+
}
|
|
201
|
+
context.drawImage(bitmap, 0, 0);
|
|
202
|
+
const imageData = context.getImageData(0, 0, bitmap.width, bitmap.height);
|
|
203
|
+
return Object.freeze({
|
|
204
|
+
width: bitmap.width,
|
|
205
|
+
height: bitmap.height,
|
|
206
|
+
data: imageData.data
|
|
207
|
+
});
|
|
208
|
+
} finally {
|
|
209
|
+
bitmap.close?.();
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
if (typeof document === "undefined") {
|
|
213
|
+
throw new Error(`Unable to decode ${urlLabel}: browser image decode APIs are unavailable.`);
|
|
214
|
+
}
|
|
215
|
+
const objectUrl = URL.createObjectURL(blob);
|
|
216
|
+
try {
|
|
217
|
+
const image = await new Promise((resolve, reject) => {
|
|
218
|
+
const element = new Image();
|
|
219
|
+
element.onload = () => resolve(element);
|
|
220
|
+
element.onerror = () => reject(new Error(`Failed to decode ${urlLabel}.`));
|
|
221
|
+
element.src = objectUrl;
|
|
222
|
+
});
|
|
223
|
+
const canvas = Object.assign(document.createElement("canvas"), {
|
|
224
|
+
width: image.naturalWidth || image.width,
|
|
225
|
+
height: image.naturalHeight || image.height
|
|
226
|
+
});
|
|
227
|
+
const context = canvas.getContext("2d", { willReadFrequently: true });
|
|
228
|
+
if (!context) {
|
|
229
|
+
throw new Error("Unable to create 2D context for glTF texture decode.");
|
|
230
|
+
}
|
|
231
|
+
context.drawImage(image, 0, 0);
|
|
232
|
+
const imageData = context.getImageData(0, 0, canvas.width, canvas.height);
|
|
233
|
+
return Object.freeze({
|
|
234
|
+
width: canvas.width,
|
|
235
|
+
height: canvas.height,
|
|
236
|
+
data: imageData.data
|
|
237
|
+
});
|
|
238
|
+
} finally {
|
|
239
|
+
URL.revokeObjectURL(objectUrl);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
function sliceBufferView(document2, bufferViewIndex, buffers) {
|
|
243
|
+
const bufferView = document2.bufferViews?.[bufferViewIndex];
|
|
244
|
+
if (!bufferView) {
|
|
245
|
+
throw new Error(`glTF bufferView ${bufferViewIndex} is missing.`);
|
|
246
|
+
}
|
|
247
|
+
const buffer = buffers[bufferView.buffer];
|
|
248
|
+
if (!buffer) {
|
|
249
|
+
throw new Error(`glTF buffer ${bufferView.buffer} is missing.`);
|
|
250
|
+
}
|
|
251
|
+
const start = bufferView.byteOffset ?? 0;
|
|
252
|
+
const end = start + (bufferView.byteLength ?? 0);
|
|
253
|
+
return buffer.slice(start, end);
|
|
254
|
+
}
|
|
255
|
+
async function loadImageResource(document2, image, index, buffers, baseUrl) {
|
|
256
|
+
if (typeof image?.uri === "string") {
|
|
257
|
+
const response = await fetch(new URL(image.uri, baseUrl));
|
|
258
|
+
if (!response.ok) {
|
|
259
|
+
throw new Error(`Failed to load glTF texture: ${response.status} ${response.statusText}`);
|
|
260
|
+
}
|
|
261
|
+
return decodeImagePixels(
|
|
262
|
+
await response.blob(),
|
|
263
|
+
`glTF texture ${index}${image.uri ? ` (${image.uri})` : ""}`
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
if (typeof image?.bufferView === "number") {
|
|
267
|
+
const bytes = sliceBufferView(document2, image.bufferView, buffers);
|
|
268
|
+
return decodeImagePixels(
|
|
269
|
+
new Blob([bytes], { type: image.mimeType ?? "application/octet-stream" }),
|
|
270
|
+
`glTF texture ${index}`
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
275
|
+
function normalizeTextureTransformPair(value, fallback) {
|
|
276
|
+
if (!Array.isArray(value) || value.length < 2) {
|
|
277
|
+
return fallback;
|
|
278
|
+
}
|
|
279
|
+
return [
|
|
280
|
+
Number.isFinite(value[0]) ? Number(value[0]) : fallback[0],
|
|
281
|
+
Number.isFinite(value[1]) ? Number(value[1]) : fallback[1]
|
|
282
|
+
];
|
|
283
|
+
}
|
|
284
|
+
function readTextureTransform(textureRef) {
|
|
285
|
+
const transformExtension = textureRef?.extensions?.KHR_texture_transform ?? null;
|
|
286
|
+
return {
|
|
287
|
+
texCoord: typeof transformExtension?.texCoord === "number" ? transformExtension.texCoord : textureRef?.texCoord ?? 0,
|
|
288
|
+
offset: normalizeTextureTransformPair(transformExtension?.offset, [0, 0]),
|
|
289
|
+
scale: normalizeTextureTransformPair(transformExtension?.scale, [1, 1]),
|
|
290
|
+
rotation: Number.isFinite(transformExtension?.rotation) ? Number(transformExtension.rotation) : 0
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
function wrapTextureCoordinate(value) {
|
|
294
|
+
return (value % 1 + 1) % 1;
|
|
295
|
+
}
|
|
296
|
+
function transformTextureCoordinate(uv, transform) {
|
|
297
|
+
const scaledU = uv[0] * transform.scale[0];
|
|
298
|
+
const scaledV = uv[1] * transform.scale[1];
|
|
299
|
+
const cosine = Math.cos(transform.rotation);
|
|
300
|
+
const sine = Math.sin(transform.rotation);
|
|
301
|
+
return [
|
|
302
|
+
scaledU * cosine - scaledV * sine + transform.offset[0],
|
|
303
|
+
scaledU * sine + scaledV * cosine + transform.offset[1]
|
|
304
|
+
];
|
|
305
|
+
}
|
|
306
|
+
function readTexturePixel(data, width, height, x, y) {
|
|
307
|
+
const clampedX = Math.min(width - 1, Math.max(0, x));
|
|
308
|
+
const clampedY = Math.min(height - 1, Math.max(0, y));
|
|
309
|
+
const offset = (clampedY * width + clampedX) * 4;
|
|
310
|
+
return [
|
|
311
|
+
data[offset] ?? 0,
|
|
312
|
+
data[offset + 1] ?? 0,
|
|
313
|
+
data[offset + 2] ?? 0,
|
|
314
|
+
data[offset + 3] ?? 255
|
|
315
|
+
];
|
|
316
|
+
}
|
|
317
|
+
function mixChannel(a, b, weight) {
|
|
318
|
+
return a + (b - a) * weight;
|
|
319
|
+
}
|
|
320
|
+
function sampleTexturePixel(data, width, height, uv) {
|
|
321
|
+
const u = wrapTextureCoordinate(uv[0]);
|
|
322
|
+
const v = wrapTextureCoordinate(uv[1]);
|
|
323
|
+
const sourceX = u * Math.max(width - 1, 0);
|
|
324
|
+
const sourceY = (1 - v) * Math.max(height - 1, 0);
|
|
325
|
+
const x0 = Math.floor(sourceX);
|
|
326
|
+
const y0 = Math.floor(sourceY);
|
|
327
|
+
const x1 = Math.min(width - 1, x0 + 1);
|
|
328
|
+
const y1 = Math.min(height - 1, y0 + 1);
|
|
329
|
+
const tx = sourceX - x0;
|
|
330
|
+
const ty = sourceY - y0;
|
|
331
|
+
const topLeft = readTexturePixel(data, width, height, x0, y0);
|
|
332
|
+
const topRight = readTexturePixel(data, width, height, x1, y0);
|
|
333
|
+
const bottomLeft = readTexturePixel(data, width, height, x0, y1);
|
|
334
|
+
const bottomRight = readTexturePixel(data, width, height, x1, y1);
|
|
335
|
+
return [0, 1, 2, 3].map((channelIndex) => {
|
|
336
|
+
const top = mixChannel(topLeft[channelIndex], topRight[channelIndex], tx);
|
|
337
|
+
const bottom = mixChannel(bottomLeft[channelIndex], bottomRight[channelIndex], tx);
|
|
338
|
+
return mixChannel(top, bottom, ty);
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
function applyTextureTransformToPixels(pixels, transform) {
|
|
342
|
+
const isIdentityTransform = transform.offset[0] === 0 && transform.offset[1] === 0 && transform.scale[0] === 1 && transform.scale[1] === 1 && transform.rotation === 0;
|
|
343
|
+
if (isIdentityTransform) {
|
|
344
|
+
return pixels;
|
|
345
|
+
}
|
|
346
|
+
const transformedData = new Uint8ClampedArray(pixels.data.length);
|
|
347
|
+
for (let y = 0; y < pixels.height; y += 1) {
|
|
348
|
+
const outputV = pixels.height > 1 ? 1 - y / (pixels.height - 1) : 0;
|
|
349
|
+
for (let x = 0; x < pixels.width; x += 1) {
|
|
350
|
+
const outputU = pixels.width > 1 ? x / (pixels.width - 1) : 0;
|
|
351
|
+
const sourcePixel = sampleTexturePixel(
|
|
352
|
+
pixels.data,
|
|
353
|
+
pixels.width,
|
|
354
|
+
pixels.height,
|
|
355
|
+
transformTextureCoordinate([outputU, outputV], transform)
|
|
356
|
+
);
|
|
357
|
+
const offset = (y * pixels.width + x) * 4;
|
|
358
|
+
transformedData[offset] = sourcePixel[0];
|
|
359
|
+
transformedData[offset + 1] = sourcePixel[1];
|
|
360
|
+
transformedData[offset + 2] = sourcePixel[2];
|
|
361
|
+
transformedData[offset + 3] = sourcePixel[3];
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
return Object.freeze({
|
|
365
|
+
width: pixels.width,
|
|
366
|
+
height: pixels.height,
|
|
367
|
+
data: transformedData
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
function getMaterialTexture(document2, textureRef, imageResources) {
|
|
371
|
+
if (!textureRef || typeof textureRef.index !== "number") {
|
|
372
|
+
return null;
|
|
373
|
+
}
|
|
374
|
+
const texture = document2.textures?.[textureRef.index] ?? null;
|
|
375
|
+
const sourceIndex = texture?.source;
|
|
376
|
+
if (typeof sourceIndex !== "number") {
|
|
377
|
+
return null;
|
|
378
|
+
}
|
|
379
|
+
const pixels = imageResources.get(sourceIndex) ?? null;
|
|
380
|
+
if (!pixels) {
|
|
381
|
+
return null;
|
|
382
|
+
}
|
|
383
|
+
const transform = readTextureTransform(textureRef);
|
|
384
|
+
const transformedPixels = applyTextureTransformToPixels(pixels, transform);
|
|
385
|
+
return Object.freeze({
|
|
386
|
+
texCoord: transform.texCoord,
|
|
387
|
+
scale: textureRef.scale,
|
|
388
|
+
strength: textureRef.strength,
|
|
389
|
+
width: transformedPixels.width,
|
|
390
|
+
height: transformedPixels.height,
|
|
391
|
+
data: transformedPixels.data
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
function getMaterialInfo(document2, primitive, imageResources) {
|
|
395
|
+
const material = document2.materials?.[primitive.material] ?? null;
|
|
396
|
+
const pbr = material?.pbrMetallicRoughness ?? null;
|
|
397
|
+
const factor = pbr?.baseColorFactor ?? [0.56, 0.33, 0.22, 1];
|
|
398
|
+
const emissive = Array.isArray(material?.emissiveFactor) ? material.emissiveFactor : [0, 0, 0];
|
|
399
|
+
const extensions = material?.extensions ?? {};
|
|
400
|
+
const specular = extensions.KHR_materials_specular ?? null;
|
|
401
|
+
const transmission = extensions.KHR_materials_transmission ?? null;
|
|
402
|
+
const ior = extensions.KHR_materials_ior ?? null;
|
|
403
|
+
const clearcoat = extensions.KHR_materials_clearcoat ?? null;
|
|
404
|
+
const sheen = extensions.KHR_materials_sheen ?? null;
|
|
405
|
+
const volume = extensions.KHR_materials_volume ?? null;
|
|
406
|
+
const iridescence = extensions.KHR_materials_iridescence ?? null;
|
|
407
|
+
const anisotropy = extensions.KHR_materials_anisotropy ?? null;
|
|
408
|
+
const dispersion = extensions.KHR_materials_dispersion ?? null;
|
|
409
|
+
return Object.freeze({
|
|
410
|
+
name: material?.name ?? "default-material",
|
|
411
|
+
color: Object.freeze({
|
|
412
|
+
r: factor[0],
|
|
413
|
+
g: factor[1],
|
|
414
|
+
b: factor[2],
|
|
415
|
+
a: factor[3] ?? 1
|
|
416
|
+
}),
|
|
417
|
+
roughness: typeof pbr?.roughnessFactor === "number" ? pbr.roughnessFactor : 0.92,
|
|
418
|
+
metallic: typeof pbr?.metallicFactor === "number" ? pbr.metallicFactor : 0.08,
|
|
419
|
+
opacity: factor[3] ?? 1,
|
|
420
|
+
emissive: Object.freeze({
|
|
421
|
+
r: emissive[0] ?? 0,
|
|
422
|
+
g: emissive[1] ?? 0,
|
|
423
|
+
b: emissive[2] ?? 0,
|
|
424
|
+
a: 1
|
|
425
|
+
}),
|
|
426
|
+
baseColorTexture: getMaterialTexture(document2, pbr?.baseColorTexture, imageResources),
|
|
427
|
+
metallicRoughnessTexture: getMaterialTexture(
|
|
428
|
+
document2,
|
|
429
|
+
pbr?.metallicRoughnessTexture,
|
|
430
|
+
imageResources
|
|
431
|
+
),
|
|
432
|
+
normalTexture: getMaterialTexture(document2, material?.normalTexture, imageResources),
|
|
433
|
+
occlusionTexture: getMaterialTexture(document2, material?.occlusionTexture, imageResources),
|
|
434
|
+
emissiveTexture: getMaterialTexture(document2, material?.emissiveTexture, imageResources),
|
|
435
|
+
specular: typeof specular?.specularFactor === "number" ? specular.specularFactor : 1,
|
|
436
|
+
specularColor: Object.freeze(
|
|
437
|
+
Array.isArray(specular?.specularColorFactor) ? [...specular.specularColorFactor] : [1, 1, 1]
|
|
438
|
+
),
|
|
439
|
+
specularTexture: getMaterialTexture(document2, specular?.specularTexture, imageResources),
|
|
440
|
+
specularColorTexture: getMaterialTexture(
|
|
441
|
+
document2,
|
|
442
|
+
specular?.specularColorTexture,
|
|
443
|
+
imageResources
|
|
444
|
+
),
|
|
445
|
+
transmission: typeof transmission?.transmissionFactor === "number" ? transmission.transmissionFactor : 0,
|
|
446
|
+
transmissionTexture: getMaterialTexture(
|
|
447
|
+
document2,
|
|
448
|
+
transmission?.transmissionTexture,
|
|
449
|
+
imageResources
|
|
450
|
+
),
|
|
451
|
+
ior: typeof ior?.ior === "number" ? ior.ior : 1.45,
|
|
452
|
+
attenuationDistance: typeof volume?.attenuationDistance === "number" ? volume.attenuationDistance : null,
|
|
453
|
+
attenuationColor: Object.freeze(
|
|
454
|
+
Array.isArray(volume?.attenuationColor) ? [...volume.attenuationColor] : [1, 1, 1]
|
|
455
|
+
),
|
|
456
|
+
thickness: typeof volume?.thicknessFactor === "number" ? volume.thicknessFactor : 0,
|
|
457
|
+
thicknessTexture: getMaterialTexture(document2, volume?.thicknessTexture, imageResources),
|
|
458
|
+
clearcoat: typeof clearcoat?.clearcoatFactor === "number" ? clearcoat.clearcoatFactor : 0,
|
|
459
|
+
clearcoatTexture: getMaterialTexture(document2, clearcoat?.clearcoatTexture, imageResources),
|
|
460
|
+
clearcoatRoughness: typeof clearcoat?.clearcoatRoughnessFactor === "number" ? clearcoat.clearcoatRoughnessFactor : 0.08,
|
|
461
|
+
clearcoatRoughnessTexture: getMaterialTexture(
|
|
462
|
+
document2,
|
|
463
|
+
clearcoat?.clearcoatRoughnessTexture,
|
|
464
|
+
imageResources
|
|
465
|
+
),
|
|
466
|
+
clearcoatNormalTexture: getMaterialTexture(
|
|
467
|
+
document2,
|
|
468
|
+
clearcoat?.clearcoatNormalTexture,
|
|
469
|
+
imageResources
|
|
470
|
+
),
|
|
471
|
+
sheenColor: Object.freeze(
|
|
472
|
+
Array.isArray(sheen?.sheenColorFactor) ? [...sheen.sheenColorFactor] : [0, 0, 0]
|
|
473
|
+
),
|
|
474
|
+
sheenColorTexture: getMaterialTexture(document2, sheen?.sheenColorTexture, imageResources),
|
|
475
|
+
sheenRoughness: typeof sheen?.sheenRoughnessFactor === "number" ? sheen.sheenRoughnessFactor : 0,
|
|
476
|
+
sheenRoughnessTexture: getMaterialTexture(
|
|
477
|
+
document2,
|
|
478
|
+
sheen?.sheenRoughnessTexture,
|
|
479
|
+
imageResources
|
|
480
|
+
),
|
|
481
|
+
iridescence: typeof iridescence?.iridescenceFactor === "number" ? iridescence.iridescenceFactor : 0,
|
|
482
|
+
iridescenceTexture: getMaterialTexture(
|
|
483
|
+
document2,
|
|
484
|
+
iridescence?.iridescenceTexture,
|
|
485
|
+
imageResources
|
|
486
|
+
),
|
|
487
|
+
iridescenceIor: typeof iridescence?.iridescenceIor === "number" ? iridescence.iridescenceIor : 1.3,
|
|
488
|
+
iridescenceThicknessMinimum: typeof iridescence?.iridescenceThicknessMinimum === "number" ? iridescence.iridescenceThicknessMinimum : 100,
|
|
489
|
+
iridescenceThicknessMaximum: typeof iridescence?.iridescenceThicknessMaximum === "number" ? iridescence.iridescenceThicknessMaximum : 400,
|
|
490
|
+
iridescenceThicknessTexture: getMaterialTexture(
|
|
491
|
+
document2,
|
|
492
|
+
iridescence?.iridescenceThicknessTexture,
|
|
493
|
+
imageResources
|
|
494
|
+
),
|
|
495
|
+
anisotropy: typeof anisotropy?.anisotropyStrength === "number" ? anisotropy.anisotropyStrength : 0,
|
|
496
|
+
anisotropyRotation: typeof anisotropy?.anisotropyRotation === "number" ? anisotropy.anisotropyRotation : 0,
|
|
497
|
+
anisotropyTexture: getMaterialTexture(document2, anisotropy?.anisotropyTexture, imageResources),
|
|
498
|
+
dispersion: typeof dispersion?.dispersion === "number" ? dispersion.dispersion : 0
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
function computeBounds(positions) {
|
|
502
|
+
const min = [
|
|
503
|
+
Number.POSITIVE_INFINITY,
|
|
504
|
+
Number.POSITIVE_INFINITY,
|
|
505
|
+
Number.POSITIVE_INFINITY
|
|
506
|
+
];
|
|
507
|
+
const max = [
|
|
508
|
+
Number.NEGATIVE_INFINITY,
|
|
509
|
+
Number.NEGATIVE_INFINITY,
|
|
510
|
+
Number.NEGATIVE_INFINITY
|
|
511
|
+
];
|
|
512
|
+
for (let index = 0; index < positions.length; index += 3) {
|
|
513
|
+
min[0] = Math.min(min[0], positions[index]);
|
|
514
|
+
min[1] = Math.min(min[1], positions[index + 1]);
|
|
515
|
+
min[2] = Math.min(min[2], positions[index + 2]);
|
|
516
|
+
max[0] = Math.max(max[0], positions[index]);
|
|
517
|
+
max[1] = Math.max(max[1], positions[index + 1]);
|
|
518
|
+
max[2] = Math.max(max[2], positions[index + 2]);
|
|
519
|
+
}
|
|
520
|
+
return Object.freeze({
|
|
521
|
+
min: Object.freeze([min[0], min[1], min[2]]),
|
|
522
|
+
max: Object.freeze([max[0], max[1], max[2]])
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
function appendValues(target, values) {
|
|
526
|
+
for (let index = 0; index < values.length; index += 1) {
|
|
527
|
+
target.push(values[index]);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
function appendIndicesWithOffset(target, values, vertexOffset) {
|
|
531
|
+
for (let index = 0; index < values.length; index += 1) {
|
|
532
|
+
target.push(values[index] + vertexOffset);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
function resolveBrowserRequestBaseUrl() {
|
|
536
|
+
if (typeof document !== "undefined" && typeof document.baseURI === "string" && document.baseURI.length > 0) {
|
|
537
|
+
return document.baseURI;
|
|
538
|
+
}
|
|
539
|
+
if (typeof window !== "undefined" && typeof window.location?.href === "string" && window.location.href.length > 0) {
|
|
540
|
+
return window.location.href;
|
|
541
|
+
}
|
|
542
|
+
return null;
|
|
543
|
+
}
|
|
544
|
+
function resolveFetchBaseUrl(requestUrl, responseUrl) {
|
|
545
|
+
if (typeof responseUrl === "string" && responseUrl.length > 0) {
|
|
546
|
+
try {
|
|
547
|
+
return new URL(responseUrl);
|
|
548
|
+
} catch {
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
try {
|
|
552
|
+
return new URL(requestUrl);
|
|
553
|
+
} catch {
|
|
554
|
+
const browserBaseUrl = resolveBrowserRequestBaseUrl();
|
|
555
|
+
if (browserBaseUrl) {
|
|
556
|
+
return new URL(requestUrl, browserBaseUrl);
|
|
557
|
+
}
|
|
558
|
+
throw new Error(
|
|
559
|
+
`Unable to resolve a stable base URL for glTF asset loading: ${String(requestUrl)}`
|
|
560
|
+
);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
function createIdentityMatrix() {
|
|
564
|
+
return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
|
|
565
|
+
}
|
|
566
|
+
function multiplyMatrices(a, b) {
|
|
567
|
+
const out = new Array(16).fill(0);
|
|
568
|
+
for (let column = 0; column < 4; column += 1) {
|
|
569
|
+
for (let row = 0; row < 4; row += 1) {
|
|
570
|
+
out[column * 4 + row] = a[0 * 4 + row] * b[column * 4 + 0] + a[1 * 4 + row] * b[column * 4 + 1] + a[2 * 4 + row] * b[column * 4 + 2] + a[3 * 4 + row] * b[column * 4 + 3];
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
return out;
|
|
574
|
+
}
|
|
575
|
+
function composeNodeMatrix(node) {
|
|
576
|
+
if (Array.isArray(node.matrix) && node.matrix.length === 16) {
|
|
577
|
+
return [...node.matrix];
|
|
578
|
+
}
|
|
579
|
+
const translation = Array.isArray(node.translation) ? node.translation : [0, 0, 0];
|
|
580
|
+
const rotation = Array.isArray(node.rotation) ? node.rotation : [0, 0, 0, 1];
|
|
581
|
+
const scale = Array.isArray(node.scale) ? node.scale : [1, 1, 1];
|
|
582
|
+
const [x, y, z, w] = rotation;
|
|
583
|
+
const x2 = x + x;
|
|
584
|
+
const y2 = y + y;
|
|
585
|
+
const z2 = z + z;
|
|
586
|
+
const xx = x * x2;
|
|
587
|
+
const xy = x * y2;
|
|
588
|
+
const xz = x * z2;
|
|
589
|
+
const yy = y * y2;
|
|
590
|
+
const yz = y * z2;
|
|
591
|
+
const zz = z * z2;
|
|
592
|
+
const wx = w * x2;
|
|
593
|
+
const wy = w * y2;
|
|
594
|
+
const wz = w * z2;
|
|
595
|
+
return [
|
|
596
|
+
(1 - (yy + zz)) * scale[0],
|
|
597
|
+
(xy + wz) * scale[0],
|
|
598
|
+
(xz - wy) * scale[0],
|
|
599
|
+
0,
|
|
600
|
+
(xy - wz) * scale[1],
|
|
601
|
+
(1 - (xx + zz)) * scale[1],
|
|
602
|
+
(yz + wx) * scale[1],
|
|
603
|
+
0,
|
|
604
|
+
(xz + wy) * scale[2],
|
|
605
|
+
(yz - wx) * scale[2],
|
|
606
|
+
(1 - (xx + yy)) * scale[2],
|
|
607
|
+
0,
|
|
608
|
+
translation[0],
|
|
609
|
+
translation[1],
|
|
610
|
+
translation[2],
|
|
611
|
+
1
|
|
612
|
+
];
|
|
613
|
+
}
|
|
614
|
+
function transformPosition(position, matrix) {
|
|
615
|
+
return [
|
|
616
|
+
matrix[0] * position[0] + matrix[4] * position[1] + matrix[8] * position[2] + matrix[12],
|
|
617
|
+
matrix[1] * position[0] + matrix[5] * position[1] + matrix[9] * position[2] + matrix[13],
|
|
618
|
+
matrix[2] * position[0] + matrix[6] * position[1] + matrix[10] * position[2] + matrix[14]
|
|
619
|
+
];
|
|
620
|
+
}
|
|
621
|
+
function transformNormal(normal, matrix) {
|
|
622
|
+
const transformed = [
|
|
623
|
+
matrix[0] * normal[0] + matrix[4] * normal[1] + matrix[8] * normal[2],
|
|
624
|
+
matrix[1] * normal[0] + matrix[5] * normal[1] + matrix[9] * normal[2],
|
|
625
|
+
matrix[2] * normal[0] + matrix[6] * normal[1] + matrix[10] * normal[2]
|
|
626
|
+
];
|
|
627
|
+
const length = Math.hypot(transformed[0], transformed[1], transformed[2]) || 1;
|
|
628
|
+
return [transformed[0] / length, transformed[1] / length, transformed[2] / length];
|
|
629
|
+
}
|
|
630
|
+
function collectScenePrimitives(document2, buffers, imageResources) {
|
|
631
|
+
const scene = document2.scenes?.[document2.scene ?? 0];
|
|
632
|
+
if (!scene || !Array.isArray(scene.nodes) || scene.nodes.length === 0) {
|
|
633
|
+
throw new Error("glTF demo asset must expose a default scene with at least one node.");
|
|
634
|
+
}
|
|
635
|
+
const results = [];
|
|
636
|
+
let modelName = null;
|
|
637
|
+
let physics = null;
|
|
638
|
+
function visit(nodeIndex, parentMatrix) {
|
|
639
|
+
const node = document2.nodes?.[nodeIndex];
|
|
640
|
+
if (!node) {
|
|
641
|
+
throw new Error(`glTF node ${nodeIndex} is missing.`);
|
|
642
|
+
}
|
|
643
|
+
const localMatrix = composeNodeMatrix(node);
|
|
644
|
+
const worldMatrix = multiplyMatrices(parentMatrix, localMatrix);
|
|
645
|
+
if (!modelName && typeof node.name === "string" && node.name.length > 0) {
|
|
646
|
+
modelName = node.name;
|
|
647
|
+
}
|
|
648
|
+
if (!physics && node.extras?.physics && typeof node.extras.physics === "object") {
|
|
649
|
+
physics = Object.freeze({ ...node.extras.physics });
|
|
650
|
+
}
|
|
651
|
+
if (typeof node.mesh === "number") {
|
|
652
|
+
const mesh = document2.meshes?.[node.mesh];
|
|
653
|
+
if (!mesh || !Array.isArray(mesh.primitives)) {
|
|
654
|
+
throw new Error(`glTF mesh ${node.mesh} is missing primitives.`);
|
|
655
|
+
}
|
|
656
|
+
mesh.primitives.forEach((primitive, primitiveIndex) => {
|
|
657
|
+
const positions = readAccessor(document2, primitive.attributes.POSITION, buffers);
|
|
658
|
+
const normals = typeof primitive.attributes.NORMAL === "number" ? readAccessor(document2, primitive.attributes.NORMAL, buffers) : null;
|
|
659
|
+
const colors = typeof primitive.attributes.COLOR_0 === "number" ? readAccessor(document2, primitive.attributes.COLOR_0, buffers) : null;
|
|
660
|
+
const uvs = typeof primitive.attributes.TEXCOORD_0 === "number" ? readAccessor(document2, primitive.attributes.TEXCOORD_0, buffers) : null;
|
|
661
|
+
const transformedPositions = [];
|
|
662
|
+
const transformedNormals = [];
|
|
663
|
+
for (let index = 0; index < positions.length; index += 3) {
|
|
664
|
+
const point = transformPosition(
|
|
665
|
+
[positions[index], positions[index + 1], positions[index + 2]],
|
|
666
|
+
worldMatrix
|
|
667
|
+
);
|
|
668
|
+
transformedPositions.push(point[0], point[1], point[2]);
|
|
669
|
+
if (normals) {
|
|
670
|
+
const normal = transformNormal(
|
|
671
|
+
[normals[index], normals[index + 1], normals[index + 2]],
|
|
672
|
+
worldMatrix
|
|
673
|
+
);
|
|
674
|
+
transformedNormals.push(normal[0], normal[1], normal[2]);
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
const indices = typeof primitive.indices === "number" ? readAccessor(document2, primitive.indices, buffers).map((value) => Number(value)) : Array.from({ length: transformedPositions.length / 3 }, (_, index) => index);
|
|
678
|
+
const material = getMaterialInfo(document2, primitive, imageResources);
|
|
679
|
+
const primitiveName = `${node.name ?? mesh.name ?? "mesh"}-${primitiveIndex}`;
|
|
680
|
+
results.push(
|
|
681
|
+
Object.freeze({
|
|
682
|
+
name: primitiveName,
|
|
683
|
+
positions: Object.freeze(transformedPositions),
|
|
684
|
+
indices: Object.freeze(indices),
|
|
685
|
+
normals: transformedNormals.length > 0 ? Object.freeze(transformedNormals) : null,
|
|
686
|
+
uvs: uvs ? Object.freeze(uvs) : null,
|
|
687
|
+
colors: colors ? Object.freeze(colors) : null,
|
|
688
|
+
material,
|
|
689
|
+
bounds: computeBounds(transformedPositions)
|
|
690
|
+
})
|
|
691
|
+
);
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
if (Array.isArray(node.children)) {
|
|
695
|
+
for (const childIndex of node.children) {
|
|
696
|
+
visit(childIndex, worldMatrix);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
for (const rootNodeIndex of scene.nodes) {
|
|
701
|
+
visit(rootNodeIndex, createIdentityMatrix());
|
|
702
|
+
}
|
|
703
|
+
if (results.length === 0) {
|
|
704
|
+
throw new Error("glTF demo asset must contain at least one mesh primitive.");
|
|
705
|
+
}
|
|
706
|
+
return {
|
|
707
|
+
name: modelName ?? "gltf-model",
|
|
708
|
+
physics: physics ?? Object.freeze({}),
|
|
709
|
+
primitives: results
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
async function loadGltfDocument(url) {
|
|
713
|
+
const response = await fetch(url);
|
|
714
|
+
if (!response.ok) {
|
|
715
|
+
throw new Error(`Failed to load glTF asset: ${response.status} ${response.statusText}`);
|
|
716
|
+
}
|
|
717
|
+
return {
|
|
718
|
+
document: await response.json(),
|
|
719
|
+
baseUrl: resolveFetchBaseUrl(url, response.url)
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
async function loadInlineShowcaseDocument() {
|
|
723
|
+
const module = await import("./showcase-inline-assets-QRQKXGVX.js");
|
|
724
|
+
return loadGltfDocument(new URL(module.INLINE_SHOWCASE_ASSET_URLS.brigantine));
|
|
725
|
+
}
|
|
726
|
+
async function buildGltfModel(document2, baseUrl) {
|
|
727
|
+
const buffers = await Promise.all(
|
|
728
|
+
(document2.buffers ?? []).map(async (buffer) => {
|
|
729
|
+
if (typeof buffer.uri !== "string") {
|
|
730
|
+
throw new Error("glTF buffer URI is required for demo asset loading.");
|
|
731
|
+
}
|
|
732
|
+
if (buffer.uri.startsWith("data:")) {
|
|
733
|
+
return decodeDataUri(buffer.uri);
|
|
734
|
+
}
|
|
735
|
+
const nested = await fetch(new URL(buffer.uri, baseUrl));
|
|
736
|
+
if (!nested.ok) {
|
|
737
|
+
throw new Error(`Failed to load glTF buffer: ${nested.status} ${nested.statusText}`);
|
|
738
|
+
}
|
|
739
|
+
return nested.arrayBuffer();
|
|
740
|
+
})
|
|
741
|
+
);
|
|
742
|
+
const imageResources = /* @__PURE__ */ new Map();
|
|
743
|
+
await Promise.all(
|
|
744
|
+
(document2.images ?? []).map(async (image, index) => {
|
|
745
|
+
const pixels = await loadImageResource(document2, image, index, buffers, baseUrl);
|
|
746
|
+
if (pixels) {
|
|
747
|
+
imageResources.set(index, pixels);
|
|
748
|
+
}
|
|
749
|
+
})
|
|
750
|
+
);
|
|
751
|
+
const scene = collectScenePrimitives(document2, buffers, imageResources);
|
|
752
|
+
const aggregatePositions = [];
|
|
753
|
+
const aggregateIndices = [];
|
|
754
|
+
for (const primitive of scene.primitives) {
|
|
755
|
+
const vertexOffset = aggregatePositions.length / 3;
|
|
756
|
+
appendValues(aggregatePositions, primitive.positions);
|
|
757
|
+
appendIndicesWithOffset(aggregateIndices, primitive.indices, vertexOffset);
|
|
758
|
+
}
|
|
759
|
+
const color = scene.primitives[0]?.material?.color ?? { r: 0.56, g: 0.33, b: 0.22, a: 1 };
|
|
760
|
+
return Object.freeze({
|
|
761
|
+
name: scene.name,
|
|
762
|
+
positions: Object.freeze(aggregatePositions),
|
|
763
|
+
indices: Object.freeze(aggregateIndices),
|
|
764
|
+
bounds: computeBounds(aggregatePositions),
|
|
765
|
+
color: Object.freeze({ ...color }),
|
|
766
|
+
physics: scene.physics,
|
|
767
|
+
primitives: Object.freeze(scene.primitives)
|
|
768
|
+
});
|
|
769
|
+
}
|
|
770
|
+
function shouldRetryWithInlineShowcaseFallback(url, error) {
|
|
771
|
+
if (!shouldUseInlineShowcaseFallback(url)) {
|
|
772
|
+
return false;
|
|
773
|
+
}
|
|
774
|
+
return error instanceof TypeError || /^Failed to load glTF asset:/u.test(error.message);
|
|
775
|
+
}
|
|
776
|
+
async function loadGltfModel(url) {
|
|
777
|
+
try {
|
|
778
|
+
const { document: document2, baseUrl } = await loadGltfDocument(url);
|
|
779
|
+
return buildGltfModel(document2, baseUrl);
|
|
780
|
+
} catch (error) {
|
|
781
|
+
if (!shouldRetryWithInlineShowcaseFallback(url, error)) {
|
|
782
|
+
throw error;
|
|
783
|
+
}
|
|
784
|
+
const { document: document2, baseUrl } = await loadInlineShowcaseDocument();
|
|
785
|
+
return buildGltfModel(document2, baseUrl);
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
export {
|
|
790
|
+
resolveShowcaseAssetUrl,
|
|
791
|
+
loadGltfModel
|
|
792
|
+
};
|
|
793
|
+
//# sourceMappingURL=chunk-UKCJ2AWJ.js.map
|