@plasius/gpu-shared 0.1.13 → 0.1.15
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 +26 -0
- package/README.md +70 -3
- package/dist/{chunk-NCPJWLX3.js → chunk-2GM64LB6.js} +1 -9
- package/dist/{chunk-NCPJWLX3.js.map → chunk-2GM64LB6.js.map} +1 -1
- package/dist/chunk-6SOHFUOE.js +403 -0
- package/dist/chunk-6SOHFUOE.js.map +1 -0
- package/dist/{chunk-DABW627O.js → chunk-CH3ZS5TQ.js} +7 -1
- package/dist/chunk-CH3ZS5TQ.js.map +1 -0
- package/dist/chunk-DGUM43GV.js +11 -0
- package/dist/{chunk-DQX4DXBR.js → chunk-QVNRTWHB.js} +82 -6
- package/dist/chunk-QVNRTWHB.js.map +1 -0
- package/dist/dist-B5R2GZQR.js +1433 -0
- package/dist/dist-B5R2GZQR.js.map +1 -0
- package/dist/gltf-loader-B6VOWGBV.js +9 -0
- package/dist/index.cjs +2408 -5913
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +55 -5
- package/dist/index.js.map +1 -1
- package/dist/product-studio-runtime-BYVBUWIN.js +12 -0
- package/dist/product-studio-runtime-BYVBUWIN.js.map +1 -0
- package/dist/showcase-inline-assets-QRQKXGVX.js +8 -0
- package/dist/showcase-inline-assets-QRQKXGVX.js.map +1 -0
- package/dist/showcase-runtime-M6TEUYOG.js +3786 -0
- package/dist/showcase-runtime-M6TEUYOG.js.map +1 -0
- package/package.json +7 -10
- package/src/feature-flags.js +1 -0
- package/src/gltf-loader.js +14 -2
- package/src/index.d.ts +73 -1
- package/src/index.js +67 -0
- package/src/product-studio-runtime.js +466 -0
- package/src/showcase-runtime.js +875 -72
- package/dist/chunk-2FIFSBB4.js +0 -74
- package/dist/chunk-2FIFSBB4.js.map +0 -1
- package/dist/chunk-DABW627O.js.map +0 -1
- package/dist/chunk-DQX4DXBR.js.map +0 -1
- package/dist/gltf-loader-WAM23F37.js +0 -9
- package/dist/showcase-inline-assets-B7U7VX5H.js +0 -7
- package/dist/showcase-runtime-PN7N3FZY.js +0 -9164
- package/dist/showcase-runtime-PN7N3FZY.js.map +0 -1
- /package/dist/{gltf-loader-WAM23F37.js.map → chunk-DGUM43GV.js.map} +0 -0
- /package/dist/{showcase-inline-assets-B7U7VX5H.js.map → gltf-loader-B6VOWGBV.js.map} +0 -0
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
import { loadGltfModel } from "./gltf-loader.js";
|
|
2
|
+
|
|
3
|
+
const STYLE_ID = "plasius-product-studio-wavefront-style";
|
|
4
|
+
const DEFAULT_PRODUCT_ASSET_URL =
|
|
5
|
+
"/data/models/eames-lounge-chair-ottoman/Eames_Lounge_Chair_Ottoman.gltf";
|
|
6
|
+
const DEFAULT_TARGET_CENTER = Object.freeze([0, 0.74, 0]);
|
|
7
|
+
const DEFAULT_TARGET_SIZE = 2.25;
|
|
8
|
+
|
|
9
|
+
function clamp(value, min, max) {
|
|
10
|
+
return Math.max(min, Math.min(max, value));
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function isFiniteVector(value) {
|
|
14
|
+
return (
|
|
15
|
+
Array.isArray(value) &&
|
|
16
|
+
value.length >= 3 &&
|
|
17
|
+
Number.isFinite(value[0]) &&
|
|
18
|
+
Number.isFinite(value[1]) &&
|
|
19
|
+
Number.isFinite(value[2])
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function createEmptyBounds() {
|
|
24
|
+
return {
|
|
25
|
+
min: [Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY],
|
|
26
|
+
max: [Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY],
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function expandBounds(bounds, point) {
|
|
31
|
+
bounds.min[0] = Math.min(bounds.min[0], point[0]);
|
|
32
|
+
bounds.min[1] = Math.min(bounds.min[1], point[1]);
|
|
33
|
+
bounds.min[2] = Math.min(bounds.min[2], point[2]);
|
|
34
|
+
bounds.max[0] = Math.max(bounds.max[0], point[0]);
|
|
35
|
+
bounds.max[1] = Math.max(bounds.max[1], point[1]);
|
|
36
|
+
bounds.max[2] = Math.max(bounds.max[2], point[2]);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function getBoundsSize(bounds) {
|
|
40
|
+
return [
|
|
41
|
+
bounds.max[0] - bounds.min[0],
|
|
42
|
+
bounds.max[1] - bounds.min[1],
|
|
43
|
+
bounds.max[2] - bounds.min[2],
|
|
44
|
+
];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function getBoundsCenter(bounds) {
|
|
48
|
+
return [
|
|
49
|
+
(bounds.min[0] + bounds.max[0]) * 0.5,
|
|
50
|
+
(bounds.min[1] + bounds.max[1]) * 0.5,
|
|
51
|
+
(bounds.min[2] + bounds.max[2]) * 0.5,
|
|
52
|
+
];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function getModelBounds(model) {
|
|
56
|
+
if (isFiniteVector(model?.bounds?.min) && isFiniteVector(model?.bounds?.max)) {
|
|
57
|
+
return {
|
|
58
|
+
min: [...model.bounds.min],
|
|
59
|
+
max: [...model.bounds.max],
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const bounds = createEmptyBounds();
|
|
64
|
+
for (const primitive of model?.primitives ?? []) {
|
|
65
|
+
for (let index = 0; index < primitive.positions.length; index += 3) {
|
|
66
|
+
expandBounds(bounds, [
|
|
67
|
+
primitive.positions[index],
|
|
68
|
+
primitive.positions[index + 1],
|
|
69
|
+
primitive.positions[index + 2],
|
|
70
|
+
]);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return bounds;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function transformPoint(point, modelCenter, scale, targetCenter) {
|
|
77
|
+
return [
|
|
78
|
+
(point[0] - modelCenter[0]) * scale + targetCenter[0],
|
|
79
|
+
(point[1] - modelCenter[1]) * scale + targetCenter[1],
|
|
80
|
+
(point[2] - modelCenter[2]) * scale + targetCenter[2],
|
|
81
|
+
];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function readMaterialColor(material) {
|
|
85
|
+
const color = material?.color ?? {};
|
|
86
|
+
return [
|
|
87
|
+
Number.isFinite(color.r) ? color.r : 0.62,
|
|
88
|
+
Number.isFinite(color.g) ? color.g : 0.56,
|
|
89
|
+
Number.isFinite(color.b) ? color.b : 0.48,
|
|
90
|
+
Number.isFinite(color.a) ? color.a : 1,
|
|
91
|
+
];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function readMaterialKind(material) {
|
|
95
|
+
const emissive = material?.emissive ?? {};
|
|
96
|
+
if ((emissive.r ?? 0) + (emissive.g ?? 0) + (emissive.b ?? 0) > 0.001) {
|
|
97
|
+
return "emissive";
|
|
98
|
+
}
|
|
99
|
+
if ((material?.metallic ?? 0) >= 0.5) {
|
|
100
|
+
return "metal";
|
|
101
|
+
}
|
|
102
|
+
const alpha = readMaterialColor(material)[3];
|
|
103
|
+
if (alpha < 0.9) {
|
|
104
|
+
return "transparent";
|
|
105
|
+
}
|
|
106
|
+
return "diffuse";
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function readEmission(material) {
|
|
110
|
+
const emissive = material?.emissive ?? {};
|
|
111
|
+
return [
|
|
112
|
+
Number.isFinite(emissive.r) ? emissive.r : 0,
|
|
113
|
+
Number.isFinite(emissive.g) ? emissive.g : 0,
|
|
114
|
+
Number.isFinite(emissive.b) ? emissive.b : 0,
|
|
115
|
+
1,
|
|
116
|
+
];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function createQuadMesh({
|
|
120
|
+
id,
|
|
121
|
+
corners,
|
|
122
|
+
color,
|
|
123
|
+
emission = [0, 0, 0, 1],
|
|
124
|
+
materialKind = "diffuse",
|
|
125
|
+
roughness = 0.72,
|
|
126
|
+
metallic = 0,
|
|
127
|
+
opacity = color[3] ?? 1,
|
|
128
|
+
}) {
|
|
129
|
+
const [a, b, c] = corners;
|
|
130
|
+
const edge1 = [b[0] - a[0], b[1] - a[1], b[2] - a[2]];
|
|
131
|
+
const edge2 = [c[0] - a[0], c[1] - a[1], c[2] - a[2]];
|
|
132
|
+
const normal = [
|
|
133
|
+
edge1[1] * edge2[2] - edge1[2] * edge2[1],
|
|
134
|
+
edge1[2] * edge2[0] - edge1[0] * edge2[2],
|
|
135
|
+
edge1[0] * edge2[1] - edge1[1] * edge2[0],
|
|
136
|
+
];
|
|
137
|
+
const length = Math.hypot(normal[0], normal[1], normal[2]) || 1;
|
|
138
|
+
const unitNormal = normal.map((value) => value / length);
|
|
139
|
+
|
|
140
|
+
return Object.freeze({
|
|
141
|
+
id,
|
|
142
|
+
positions: Object.freeze(corners.flat()),
|
|
143
|
+
indices: Object.freeze([0, 1, 2, 0, 2, 3]),
|
|
144
|
+
normals: Object.freeze([unitNormal, unitNormal, unitNormal, unitNormal].flat()),
|
|
145
|
+
color: Object.freeze([...color]),
|
|
146
|
+
emission: Object.freeze([...emission]),
|
|
147
|
+
materialKind,
|
|
148
|
+
roughness,
|
|
149
|
+
metallic,
|
|
150
|
+
opacity,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function createProductStudioEnvironmentMeshes() {
|
|
155
|
+
return [
|
|
156
|
+
createQuadMesh({
|
|
157
|
+
id: 1,
|
|
158
|
+
corners: [
|
|
159
|
+
[-3.2, -0.08, 2.4],
|
|
160
|
+
[3.2, -0.08, 2.4],
|
|
161
|
+
[3.2, -0.08, -3.1],
|
|
162
|
+
[-3.2, -0.08, -3.1],
|
|
163
|
+
],
|
|
164
|
+
color: [0.48, 0.55, 0.55, 1],
|
|
165
|
+
roughness: 0.82,
|
|
166
|
+
}),
|
|
167
|
+
createQuadMesh({
|
|
168
|
+
id: 2,
|
|
169
|
+
corners: [
|
|
170
|
+
[-3.2, -0.08, -2.45],
|
|
171
|
+
[3.2, -0.08, -2.45],
|
|
172
|
+
[3.2, 2.65, -2.45],
|
|
173
|
+
[-3.2, 2.65, -2.45],
|
|
174
|
+
],
|
|
175
|
+
color: [0.43, 0.42, 0.38, 1],
|
|
176
|
+
roughness: 0.86,
|
|
177
|
+
}),
|
|
178
|
+
createQuadMesh({
|
|
179
|
+
id: 3,
|
|
180
|
+
corners: [
|
|
181
|
+
[-2.85, -0.08, -2.45],
|
|
182
|
+
[-2.85, 2.55, -2.45],
|
|
183
|
+
[-2.85, 2.55, 2.15],
|
|
184
|
+
[-2.85, -0.08, 2.15],
|
|
185
|
+
],
|
|
186
|
+
color: [0.36, 0.42, 0.45, 1],
|
|
187
|
+
roughness: 0.8,
|
|
188
|
+
}),
|
|
189
|
+
createQuadMesh({
|
|
190
|
+
id: 4,
|
|
191
|
+
corners: [
|
|
192
|
+
[0.78, 2.55, -0.82],
|
|
193
|
+
[-0.78, 2.55, -0.82],
|
|
194
|
+
[-0.78, 2.55, -1.78],
|
|
195
|
+
[0.78, 2.55, -1.78],
|
|
196
|
+
],
|
|
197
|
+
color: [1, 0.94, 0.78, 1],
|
|
198
|
+
emission: [8.5, 7.2, 4.8, 1],
|
|
199
|
+
materialKind: "emissive",
|
|
200
|
+
roughness: 0,
|
|
201
|
+
}),
|
|
202
|
+
];
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function createProductStudioMeshFromPrimitive(primitive, primitiveIndex, transform) {
|
|
206
|
+
if (!Array.isArray(primitive?.positions) || primitive.positions.length < 9) {
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const positions = [];
|
|
211
|
+
for (let index = 0; index < primitive.positions.length; index += 3) {
|
|
212
|
+
const point = transform([
|
|
213
|
+
primitive.positions[index],
|
|
214
|
+
primitive.positions[index + 1],
|
|
215
|
+
primitive.positions[index + 2],
|
|
216
|
+
]);
|
|
217
|
+
positions.push(point[0], point[1], point[2]);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const indices =
|
|
221
|
+
Array.isArray(primitive.indices) && primitive.indices.length >= 3
|
|
222
|
+
? [...primitive.indices]
|
|
223
|
+
: Array.from({ length: positions.length / 3 }, (_, index) => index);
|
|
224
|
+
const material = primitive.material ?? {};
|
|
225
|
+
const color = readMaterialColor(material);
|
|
226
|
+
|
|
227
|
+
return Object.freeze({
|
|
228
|
+
id: 1000 + primitiveIndex,
|
|
229
|
+
positions: Object.freeze(positions),
|
|
230
|
+
indices: Object.freeze(indices),
|
|
231
|
+
normals: Array.isArray(primitive.normals) ? Object.freeze([...primitive.normals]) : null,
|
|
232
|
+
color: Object.freeze(color),
|
|
233
|
+
emission: Object.freeze(readEmission(material)),
|
|
234
|
+
materialKind: readMaterialKind(material),
|
|
235
|
+
materialRefId: 1000 + primitiveIndex,
|
|
236
|
+
roughness: Number.isFinite(material.roughness) ? material.roughness : 0.72,
|
|
237
|
+
metallic: Number.isFinite(material.metallic) ? material.metallic : 0,
|
|
238
|
+
opacity: color[3],
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export function createProductStudioMeshes(model, options = {}) {
|
|
243
|
+
const primitives = Array.isArray(model?.primitives) ? model.primitives : [];
|
|
244
|
+
if (primitives.length === 0) {
|
|
245
|
+
throw new Error("Product Studio model must contain at least one renderable primitive.");
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const targetCenter = isFiniteVector(options.targetCenter)
|
|
249
|
+
? [...options.targetCenter]
|
|
250
|
+
: [...DEFAULT_TARGET_CENTER];
|
|
251
|
+
const targetSize = Number.isFinite(options.targetSize)
|
|
252
|
+
? Math.max(options.targetSize, 0.25)
|
|
253
|
+
: DEFAULT_TARGET_SIZE;
|
|
254
|
+
const modelBounds = getModelBounds(model);
|
|
255
|
+
const modelSize = getBoundsSize(modelBounds);
|
|
256
|
+
const modelCenter = getBoundsCenter(modelBounds);
|
|
257
|
+
const scale = targetSize / Math.max(modelSize[0], modelSize[1], modelSize[2], 0.000001);
|
|
258
|
+
const transform = (point) => transformPoint(point, modelCenter, scale, targetCenter);
|
|
259
|
+
const modelMeshes = primitives
|
|
260
|
+
.map((primitive, index) => createProductStudioMeshFromPrimitive(primitive, index, transform))
|
|
261
|
+
.filter(Boolean);
|
|
262
|
+
|
|
263
|
+
return Object.freeze([...createProductStudioEnvironmentMeshes(), ...modelMeshes]);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function ensureStyles(documentRef) {
|
|
267
|
+
if (documentRef.getElementById?.(STYLE_ID)) {
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
const style = documentRef.createElement("style");
|
|
271
|
+
style.id = STYLE_ID;
|
|
272
|
+
style.textContent = `
|
|
273
|
+
.plasius-product-studio-wavefront {
|
|
274
|
+
position: relative;
|
|
275
|
+
width: 100%;
|
|
276
|
+
min-height: 420px;
|
|
277
|
+
overflow: hidden;
|
|
278
|
+
background: #0f1418;
|
|
279
|
+
display: grid;
|
|
280
|
+
place-items: center;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.plasius-product-studio-wavefront canvas {
|
|
284
|
+
display: block;
|
|
285
|
+
width: 100%;
|
|
286
|
+
height: auto;
|
|
287
|
+
max-height: 100%;
|
|
288
|
+
aspect-ratio: 16 / 9;
|
|
289
|
+
min-height: 420px;
|
|
290
|
+
object-fit: contain;
|
|
291
|
+
}
|
|
292
|
+
`;
|
|
293
|
+
documentRef.head?.appendChild?.(style);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function resolveRoot(options) {
|
|
297
|
+
const documentRef = options.document ?? globalThis.document;
|
|
298
|
+
if (options.root) {
|
|
299
|
+
return options.root;
|
|
300
|
+
}
|
|
301
|
+
const root =
|
|
302
|
+
documentRef?.querySelector?.("[data-plasius-gpu-product-studio]") ?? documentRef?.body;
|
|
303
|
+
if (!root) {
|
|
304
|
+
throw new Error("Product Studio requires a DOM root.");
|
|
305
|
+
}
|
|
306
|
+
return root;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function resolveRenderSize(root, options) {
|
|
310
|
+
const rect = root.getBoundingClientRect?.() ?? { width: 1280, height: 720 };
|
|
311
|
+
const devicePixelRatio =
|
|
312
|
+
Number.isFinite(options.devicePixelRatio)
|
|
313
|
+
? options.devicePixelRatio
|
|
314
|
+
: Number.isFinite(globalThis.window?.devicePixelRatio)
|
|
315
|
+
? globalThis.window.devicePixelRatio
|
|
316
|
+
: 1;
|
|
317
|
+
const cssWidth = Number.isFinite(rect.width) && rect.width > 0 ? rect.width : 1280;
|
|
318
|
+
const cssHeight =
|
|
319
|
+
Number.isFinite(rect.height) && rect.height > 0 ? rect.height : cssWidth * (9 / 16);
|
|
320
|
+
const width = Number.isFinite(options.width)
|
|
321
|
+
? Math.trunc(options.width)
|
|
322
|
+
: clamp(Math.round(cssWidth * devicePixelRatio), 640, 1920);
|
|
323
|
+
const height = Number.isFinite(options.height)
|
|
324
|
+
? Math.trunc(options.height)
|
|
325
|
+
: clamp(Math.round(cssHeight * devicePixelRatio), 360, 1080);
|
|
326
|
+
|
|
327
|
+
return {
|
|
328
|
+
width,
|
|
329
|
+
height,
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function installSnapshotHook(state) {
|
|
334
|
+
if (typeof globalThis.window === "undefined") {
|
|
335
|
+
return () => {};
|
|
336
|
+
}
|
|
337
|
+
const previous = globalThis.window.render_game_to_text;
|
|
338
|
+
globalThis.window.render_game_to_text = () =>
|
|
339
|
+
JSON.stringify({
|
|
340
|
+
surface: "gpu-product-studio-wavefront",
|
|
341
|
+
model: state.modelName,
|
|
342
|
+
sourceTriangles: state.sourceTriangleCount,
|
|
343
|
+
meshCount: state.meshCount,
|
|
344
|
+
geometryMode: state.geometryMode,
|
|
345
|
+
requiresTriangleMeshRenderer: state.requiresTriangleMeshRenderer,
|
|
346
|
+
displayQuality: state.displayQuality,
|
|
347
|
+
requiresMeshBvhForDisplayQuality: state.requiresMeshBvhForDisplayQuality,
|
|
348
|
+
renderer: state.rendererStats,
|
|
349
|
+
});
|
|
350
|
+
return () => {
|
|
351
|
+
if (previous === undefined) {
|
|
352
|
+
delete globalThis.window.render_game_to_text;
|
|
353
|
+
} else {
|
|
354
|
+
globalThis.window.render_game_to_text = previous;
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function countSourceTriangles(model) {
|
|
360
|
+
return (model.primitives ?? []).reduce(
|
|
361
|
+
(total, primitive) => total + Math.floor((primitive.indices?.length ?? 0) / 3),
|
|
362
|
+
0
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
async function resolveWavefrontLightingOptions(options) {
|
|
367
|
+
const fallback = {
|
|
368
|
+
environmentColor: [0.35, 0.43, 0.49, 1],
|
|
369
|
+
ambientColor: [0.02, 0.024, 0.028, 1],
|
|
370
|
+
};
|
|
371
|
+
const lightingLoader =
|
|
372
|
+
typeof options.__lightingLoader === "function"
|
|
373
|
+
? options.__lightingLoader
|
|
374
|
+
: () => import("@plasius/gpu-lighting").catch(() => null);
|
|
375
|
+
const lightingModule = await lightingLoader();
|
|
376
|
+
|
|
377
|
+
if (
|
|
378
|
+
typeof lightingModule?.createWavefrontEnvironmentLightingOptions !== "function"
|
|
379
|
+
) {
|
|
380
|
+
return fallback;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
return lightingModule.createWavefrontEnvironmentLightingOptions({
|
|
384
|
+
preset: options.lightingPreset ?? "product-studio",
|
|
385
|
+
intensity: options.lightingIntensity,
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
export async function mountGpuProductStudio(options = {}, featureFlags = null) {
|
|
390
|
+
const root = resolveRoot(options);
|
|
391
|
+
const documentRef = options.document ?? root.ownerDocument ?? globalThis.document;
|
|
392
|
+
ensureStyles(documentRef);
|
|
393
|
+
const previousMarkup = root.innerHTML;
|
|
394
|
+
root.innerHTML = "";
|
|
395
|
+
root.classList?.add?.("plasius-product-studio-wavefront");
|
|
396
|
+
|
|
397
|
+
const canvas = documentRef.createElement("canvas");
|
|
398
|
+
canvas.dataset.plasiusGpuProductStudio = "wavefront";
|
|
399
|
+
root.appendChild(canvas);
|
|
400
|
+
|
|
401
|
+
const modelLoader =
|
|
402
|
+
typeof options.__modelLoader === "function" ? options.__modelLoader : loadGltfModel;
|
|
403
|
+
const rendererLoader =
|
|
404
|
+
typeof options.__rendererLoader === "function"
|
|
405
|
+
? options.__rendererLoader
|
|
406
|
+
: () => import("@plasius/gpu-renderer");
|
|
407
|
+
const assetUrl = options.productAssetUrl ?? options.assetUrl ?? DEFAULT_PRODUCT_ASSET_URL;
|
|
408
|
+
const model = await modelLoader(assetUrl);
|
|
409
|
+
const meshes = createProductStudioMeshes(model, {
|
|
410
|
+
targetCenter: options.targetCenter,
|
|
411
|
+
targetSize: options.targetSize,
|
|
412
|
+
});
|
|
413
|
+
const rendererModule = await rendererLoader();
|
|
414
|
+
if (typeof rendererModule.createWavefrontPathTracingComputeRenderer !== "function") {
|
|
415
|
+
throw new Error("Product Studio renderer loader must provide createWavefrontPathTracingComputeRenderer.");
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
const size = resolveRenderSize(root, options);
|
|
419
|
+
const lightingOptions = await resolveWavefrontLightingOptions(options);
|
|
420
|
+
const renderer = await rendererModule.createWavefrontPathTracingComputeRenderer({
|
|
421
|
+
canvas,
|
|
422
|
+
width: size.width,
|
|
423
|
+
height: size.height,
|
|
424
|
+
maxDepth: Number.isFinite(options.maxDepth) ? options.maxDepth : 6,
|
|
425
|
+
tileSize: Number.isFinite(options.tileSize) ? options.tileSize : 128,
|
|
426
|
+
samplesPerPixel: Number.isFinite(options.samplesPerPixel) ? options.samplesPerPixel : 8,
|
|
427
|
+
denoise: options.denoise !== false,
|
|
428
|
+
displayQuality: true,
|
|
429
|
+
meshes,
|
|
430
|
+
camera: {
|
|
431
|
+
position: [0, 1.12, 5.05],
|
|
432
|
+
target: [0, 0.72, 0],
|
|
433
|
+
up: [0, 1, 0],
|
|
434
|
+
fovYDegrees: 43,
|
|
435
|
+
},
|
|
436
|
+
...lightingOptions,
|
|
437
|
+
});
|
|
438
|
+
const rendererStats = renderer.renderOnce();
|
|
439
|
+
const state = Object.freeze({
|
|
440
|
+
featureFlags,
|
|
441
|
+
modelName: model.name,
|
|
442
|
+
sourceTriangleCount: countSourceTriangles(model),
|
|
443
|
+
meshCount: meshes.length,
|
|
444
|
+
geometryMode: "mesh-bvh-display-quality",
|
|
445
|
+
requiresTriangleMeshRenderer: true,
|
|
446
|
+
displayQuality: true,
|
|
447
|
+
requiresMeshBvhForDisplayQuality: true,
|
|
448
|
+
rendererStats,
|
|
449
|
+
});
|
|
450
|
+
const restoreSnapshotHook = installSnapshotHook(state);
|
|
451
|
+
|
|
452
|
+
return Object.freeze({
|
|
453
|
+
state,
|
|
454
|
+
model,
|
|
455
|
+
productModel: model,
|
|
456
|
+
canvas,
|
|
457
|
+
renderer,
|
|
458
|
+
meshes,
|
|
459
|
+
destroy() {
|
|
460
|
+
restoreSnapshotHook();
|
|
461
|
+
renderer.destroy();
|
|
462
|
+
root.classList?.remove?.("plasius-product-studio-wavefront");
|
|
463
|
+
root.innerHTML = previousMarkup;
|
|
464
|
+
},
|
|
465
|
+
});
|
|
466
|
+
}
|