@make-pictures/kits 0.0.0-stage → 2.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.
Files changed (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +49 -2
  3. package/dist/chunk-3SUIELJV.mjs +601 -0
  4. package/dist/chunk-4Z3G6FPZ.mjs +485 -0
  5. package/dist/chunk-AHA23PHC.mjs +678 -0
  6. package/dist/chunk-CW623YVB.mjs +1124 -0
  7. package/dist/chunk-NNA7XXQB.mjs +1059 -0
  8. package/dist/chunk-S4CHTWOS.mjs +1237 -0
  9. package/dist/chunk-WAA7P5FT.mjs +912 -0
  10. package/dist/data-storytelling/AnimatedBarRace.d.ts +37 -0
  11. package/dist/data-storytelling/MetricComparisonCard.d.ts +29 -0
  12. package/dist/data-storytelling/TimelineTrack.d.ts +29 -0
  13. package/dist/data-storytelling/index.cjs +1224 -0
  14. package/dist/data-storytelling/index.d.ts +3 -0
  15. package/dist/data-storytelling/index.mjs +10 -0
  16. package/dist/index.cjs +6010 -0
  17. package/dist/index.d.ts +7 -0
  18. package/dist/index.mjs +108 -0
  19. package/dist/isometric-3d/IsometricStudioEnvironment.d.ts +8 -0
  20. package/dist/isometric-3d/Stylized3DCharacter.d.ts +29 -0
  21. package/dist/isometric-3d/index.cjs +611 -0
  22. package/dist/isometric-3d/index.d.ts +2 -0
  23. package/dist/isometric-3d/index.mjs +8 -0
  24. package/dist/kinetic-typography/AnimatedMetricCounter.d.ts +36 -0
  25. package/dist/kinetic-typography/ImpactKeywordCard.d.ts +31 -0
  26. package/dist/kinetic-typography/WordByWordStage.d.ts +47 -0
  27. package/dist/kinetic-typography/index.cjs +665 -0
  28. package/dist/kinetic-typography/index.d.ts +3 -0
  29. package/dist/kinetic-typography/index.mjs +14 -0
  30. package/dist/motion-diagrams-2d/CameraRig.d.ts +34 -0
  31. package/dist/motion-diagrams-2d/CodePayloadToast.d.ts +14 -0
  32. package/dist/motion-diagrams-2d/DataFlowEdge.d.ts +24 -0
  33. package/dist/motion-diagrams-2d/NodeBox.d.ts +65 -0
  34. package/dist/motion-diagrams-2d/SystemNode.d.ts +21 -0
  35. package/dist/motion-diagrams-2d/index.cjs +1137 -0
  36. package/dist/motion-diagrams-2d/index.d.ts +5 -0
  37. package/dist/motion-diagrams-2d/index.mjs +28 -0
  38. package/dist/paper-diorama-3d/ImageCutout3D.d.ts +47 -0
  39. package/dist/paper-diorama-3d/PaperCutout3D.d.ts +62 -0
  40. package/dist/paper-diorama-3d/PaperPopUpBook3D.d.ts +51 -0
  41. package/dist/paper-diorama-3d/PaperStageLighting3D.d.ts +12 -0
  42. package/dist/paper-diorama-3d/index.cjs +1087 -0
  43. package/dist/paper-diorama-3d/index.d.ts +4 -0
  44. package/dist/paper-diorama-3d/index.mjs +34 -0
  45. package/dist/product-frames/BrowserFrame.d.ts +28 -0
  46. package/dist/product-frames/PhoneFrame.d.ts +23 -0
  47. package/dist/product-frames/ScreenSpotlightOverlay.d.ts +34 -0
  48. package/dist/product-frames/frameMedia.d.ts +24 -0
  49. package/dist/product-frames/index.cjs +507 -0
  50. package/dist/product-frames/index.d.ts +8 -0
  51. package/dist/product-frames/index.mjs +12 -0
  52. package/dist/vfx/CinematicLensOverlay.d.ts +29 -0
  53. package/dist/vfx/ParticleField.d.ts +18 -0
  54. package/dist/vfx/SceneTransition.d.ts +24 -0
  55. package/dist/vfx/StagePostProcessing3D.d.ts +44 -0
  56. package/dist/vfx/index.cjs +933 -0
  57. package/dist/vfx/index.d.ts +4 -0
  58. package/dist/vfx/index.mjs +14 -0
  59. package/package.json +95 -4
@@ -0,0 +1,1059 @@
1
+ // packages/kits/src/paper-diorama-3d/PaperCutout3D.tsx
2
+ import { useEffect, useMemo } from "react";
3
+ import * as THREE from "three";
4
+ import {
5
+ createSeededRandom,
6
+ hashToHex,
7
+ useStudioFontsReady
8
+ } from "@make-pictures/core";
9
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
10
+ var MAX_CACHED_CUTOUTS = 64;
11
+ var TEXTURE_CACHE = /* @__PURE__ */ new Map();
12
+ var scheduledPruneTimer = null;
13
+ function touchCacheEntry(compositeKey, entry) {
14
+ TEXTURE_CACHE.delete(compositeKey);
15
+ TEXTURE_CACHE.set(compositeKey, entry);
16
+ }
17
+ function disposeCacheEntry(entry) {
18
+ if (entry.disposed) return;
19
+ entry.disposed = true;
20
+ entry.frontTexture.dispose();
21
+ entry.backTexture.dispose();
22
+ entry.depthMaterial.dispose();
23
+ }
24
+ function prunePaperTextureCache(maxEntries = MAX_CACHED_CUTOUTS) {
25
+ if (TEXTURE_CACHE.size <= maxEntries) return 0;
26
+ let evicted = 0;
27
+ for (const [key, entry] of TEXTURE_CACHE.entries()) {
28
+ if (TEXTURE_CACHE.size <= maxEntries) break;
29
+ if (entry.refCount <= 0) {
30
+ TEXTURE_CACHE.delete(key);
31
+ disposeCacheEntry(entry);
32
+ evicted += 1;
33
+ }
34
+ }
35
+ return evicted;
36
+ }
37
+ function scheduleCachePrune() {
38
+ if (scheduledPruneTimer !== null) return;
39
+ scheduledPruneTimer = setTimeout(() => {
40
+ scheduledPruneTimer = null;
41
+ prunePaperTextureCache();
42
+ }, 60);
43
+ }
44
+ function getPaperTextureCacheStats() {
45
+ let activeCount = 0;
46
+ for (const entry of TEXTURE_CACHE.values()) {
47
+ if (entry.refCount > 0) activeCount += 1;
48
+ }
49
+ return {
50
+ size: TEXTURE_CACHE.size,
51
+ activeCount,
52
+ maxEntries: MAX_CACHED_CUTOUTS
53
+ };
54
+ }
55
+ var DRAW_FN_HASH_CACHE = /* @__PURE__ */ new WeakMap();
56
+ function getDrawFunctionHash(draw) {
57
+ const cached = DRAW_FN_HASH_CACHE.get(draw);
58
+ if (cached !== void 0) return cached;
59
+ const computed = hashToHex(draw.toString());
60
+ DRAW_FN_HASH_CACHE.set(draw, computed);
61
+ return computed;
62
+ }
63
+ function serializePaperCutoutDeps(cacheDeps) {
64
+ return cacheDeps && cacheDeps.length > 0 ? cacheDeps.map((d) => String(d ?? "")).join("") : "";
65
+ }
66
+ function buildPaperCutoutCompositeKey(cacheKey, pixelWidth, pixelHeight, whiteBorderPx, fontRevision, draw, cacheDeps) {
67
+ const depsSignature = serializePaperCutoutDeps(cacheDeps);
68
+ const fnHash = getDrawFunctionHash(draw);
69
+ const codeAndDepsHash = depsSignature ? hashToHex(`${fnHash}${depsSignature}`) : fnHash;
70
+ return `${cacheKey}|${pixelWidth}x${pixelHeight}|b${whiteBorderPx}|f${fontRevision}|h${codeAndDepsHash}`;
71
+ }
72
+ function getOrCreatePaperCutoutTextures(compositeKey, seedKey, pixelWidth, pixelHeight, whiteBorderPx, draw) {
73
+ const existing = TEXTURE_CACHE.get(compositeKey);
74
+ if (existing && !existing.disposed) {
75
+ touchCacheEntry(compositeKey, existing);
76
+ return existing;
77
+ }
78
+ const artCanvas = document.createElement("canvas");
79
+ artCanvas.width = pixelWidth;
80
+ artCanvas.height = pixelHeight;
81
+ const artCtx = artCanvas.getContext("2d");
82
+ artCtx.lineCap = "round";
83
+ artCtx.lineJoin = "round";
84
+ draw(artCtx, pixelWidth, pixelHeight);
85
+ const whiteStampCanvas = document.createElement("canvas");
86
+ whiteStampCanvas.width = pixelWidth;
87
+ whiteStampCanvas.height = pixelHeight;
88
+ const whiteCtx = whiteStampCanvas.getContext("2d");
89
+ whiteCtx.drawImage(artCanvas, 0, 0);
90
+ whiteCtx.globalCompositeOperation = "source-in";
91
+ whiteCtx.fillStyle = "#FFFDF7";
92
+ whiteCtx.fillRect(0, 0, pixelWidth, pixelHeight);
93
+ const edgeStampCanvas = document.createElement("canvas");
94
+ edgeStampCanvas.width = pixelWidth;
95
+ edgeStampCanvas.height = pixelHeight;
96
+ const edgeCtx = edgeStampCanvas.getContext("2d");
97
+ edgeCtx.drawImage(artCanvas, 0, 0);
98
+ edgeCtx.globalCompositeOperation = "source-in";
99
+ edgeCtx.fillStyle = "#573B26";
100
+ edgeCtx.fillRect(0, 0, pixelWidth, pixelHeight);
101
+ const frontCanvas = document.createElement("canvas");
102
+ frontCanvas.width = pixelWidth;
103
+ frontCanvas.height = pixelHeight;
104
+ const frontCtx = frontCanvas.getContext("2d");
105
+ if (whiteBorderPx > 0) {
106
+ const outerRadius = whiteBorderPx + 3.5;
107
+ const steps = 20;
108
+ for (let i = 0; i < steps; i++) {
109
+ const angle = i / steps * Math.PI * 2;
110
+ frontCtx.drawImage(
111
+ edgeStampCanvas,
112
+ Math.cos(angle) * outerRadius,
113
+ Math.sin(angle) * outerRadius
114
+ );
115
+ }
116
+ for (const r of [whiteBorderPx, whiteBorderPx * 0.55]) {
117
+ for (let i = 0; i < steps; i++) {
118
+ const angle = i / steps * Math.PI * 2;
119
+ frontCtx.drawImage(
120
+ whiteStampCanvas,
121
+ Math.cos(angle) * r,
122
+ Math.sin(angle) * r
123
+ );
124
+ }
125
+ }
126
+ }
127
+ frontCtx.drawImage(artCanvas, 0, 0);
128
+ const rng = createSeededRandom(`${seedKey}:${pixelWidth}x${pixelHeight}`);
129
+ frontCtx.save();
130
+ frontCtx.globalCompositeOperation = "source-atop";
131
+ for (let i = 0; i < 220; i++) {
132
+ const gx = rng() * pixelWidth;
133
+ const gy = rng() * pixelHeight;
134
+ const fiberLen = 12 + rng() * 14;
135
+ const fiberH = 1.2 + rng() * 1.1;
136
+ const tilt = (rng() - 0.5) * 0.36;
137
+ frontCtx.save();
138
+ frontCtx.translate(gx, gy);
139
+ frontCtx.rotate(tilt);
140
+ frontCtx.fillStyle = i % 2 === 0 ? "rgba(255, 253, 245, 0.036)" : "rgba(120, 53, 15, 0.028)";
141
+ frontCtx.fillRect(-fiberLen * 0.5, -fiberH * 0.5, fiberLen, fiberH);
142
+ frontCtx.restore();
143
+ }
144
+ frontCtx.restore();
145
+ const backCanvas = document.createElement("canvas");
146
+ backCanvas.width = pixelWidth;
147
+ backCanvas.height = pixelHeight;
148
+ const backCtx = backCanvas.getContext("2d");
149
+ backCtx.drawImage(frontCanvas, 0, 0);
150
+ backCtx.globalCompositeOperation = "source-in";
151
+ backCtx.fillStyle = "#E6D7C3";
152
+ backCtx.fillRect(0, 0, pixelWidth, pixelHeight);
153
+ backCtx.globalCompositeOperation = "source-over";
154
+ artCanvas.width = 0;
155
+ artCanvas.height = 0;
156
+ whiteStampCanvas.width = 0;
157
+ whiteStampCanvas.height = 0;
158
+ edgeStampCanvas.width = 0;
159
+ edgeStampCanvas.height = 0;
160
+ const frontTexture = new THREE.CanvasTexture(frontCanvas);
161
+ frontTexture.colorSpace = THREE.SRGBColorSpace;
162
+ frontTexture.anisotropy = 8;
163
+ frontTexture.needsUpdate = true;
164
+ const backTexture = new THREE.CanvasTexture(backCanvas);
165
+ backTexture.colorSpace = THREE.SRGBColorSpace;
166
+ backTexture.anisotropy = 4;
167
+ backTexture.needsUpdate = true;
168
+ const depthMaterial = new THREE.MeshDepthMaterial({
169
+ depthPacking: THREE.RGBADepthPacking,
170
+ map: frontTexture,
171
+ alphaTest: 0.38
172
+ });
173
+ const result = {
174
+ frontTexture,
175
+ backTexture,
176
+ depthMaterial,
177
+ refCount: 0,
178
+ disposed: false
179
+ };
180
+ TEXTURE_CACHE.set(compositeKey, result);
181
+ prunePaperTextureCache();
182
+ return result;
183
+ }
184
+ var PaperCutout3D = ({
185
+ cacheKey,
186
+ cacheDeps,
187
+ pixelWidth = 512,
188
+ pixelHeight = 512,
189
+ width,
190
+ height,
191
+ draw,
192
+ whiteBorderPx = 14,
193
+ position = [0, 0, 0],
194
+ rotation = [0, 0, 0],
195
+ scale = 1,
196
+ anchor = "bottom",
197
+ thickness = 0.038,
198
+ castShadow = true,
199
+ receiveShadow = true,
200
+ emissive = "#000000",
201
+ emissiveIntensity = 0,
202
+ opacity = 1
203
+ }) => {
204
+ const fontRevision = useStudioFontsReady();
205
+ const depsSignature = serializePaperCutoutDeps(cacheDeps);
206
+ const drawFnHash = getDrawFunctionHash(draw);
207
+ const compositeKey = useMemo(() => {
208
+ const codeAndDepsHash = depsSignature ? hashToHex(`${drawFnHash}${depsSignature}`) : drawFnHash;
209
+ return `${cacheKey}|${pixelWidth}x${pixelHeight}|b${whiteBorderPx}|f${fontRevision}|h${codeAndDepsHash}`;
210
+ }, [
211
+ cacheKey,
212
+ pixelWidth,
213
+ pixelHeight,
214
+ whiteBorderPx,
215
+ fontRevision,
216
+ drawFnHash,
217
+ depsSignature
218
+ ]);
219
+ const cachedEntry = useMemo(
220
+ () => getOrCreatePaperCutoutTextures(
221
+ compositeKey,
222
+ cacheKey,
223
+ pixelWidth,
224
+ pixelHeight,
225
+ whiteBorderPx,
226
+ draw
227
+ ),
228
+ // eslint-disable-next-line react-hooks/exhaustive-deps
229
+ [compositeKey]
230
+ );
231
+ useEffect(() => {
232
+ cachedEntry.refCount += 1;
233
+ touchCacheEntry(compositeKey, cachedEntry);
234
+ return () => {
235
+ cachedEntry.refCount = Math.max(0, cachedEntry.refCount - 1);
236
+ scheduleCachePrune();
237
+ };
238
+ }, [compositeKey, cachedEntry]);
239
+ const { frontTexture, backTexture, depthMaterial } = cachedEntry;
240
+ const yOffset = anchor === "bottom" ? height / 2 : anchor === "top" ? -height / 2 : 0;
241
+ return /* @__PURE__ */ jsxs("group", { position, rotation, scale, children: [
242
+ /* @__PURE__ */ jsxs(
243
+ "mesh",
244
+ {
245
+ position: [0, yOffset, 0],
246
+ castShadow,
247
+ receiveShadow,
248
+ customDepthMaterial: depthMaterial,
249
+ children: [
250
+ /* @__PURE__ */ jsx("planeGeometry", { args: [width, height] }),
251
+ /* @__PURE__ */ jsx(
252
+ "meshStandardMaterial",
253
+ {
254
+ map: frontTexture,
255
+ transparent: true,
256
+ alphaTest: 0.35,
257
+ opacity,
258
+ roughness: 0.86,
259
+ metalness: 0.04,
260
+ emissive,
261
+ emissiveIntensity,
262
+ side: THREE.DoubleSide
263
+ }
264
+ )
265
+ ]
266
+ }
267
+ ),
268
+ thickness > 5e-3 && opacity > 0.5 && /* @__PURE__ */ jsxs(Fragment, { children: [
269
+ /* @__PURE__ */ jsxs(
270
+ "mesh",
271
+ {
272
+ position: [0, yOffset, -thickness * 0.5],
273
+ receiveShadow,
274
+ children: [
275
+ /* @__PURE__ */ jsx("planeGeometry", { args: [width, height] }),
276
+ /* @__PURE__ */ jsx(
277
+ "meshStandardMaterial",
278
+ {
279
+ map: backTexture,
280
+ transparent: true,
281
+ alphaTest: 0.38,
282
+ roughness: 0.94,
283
+ metalness: 0,
284
+ side: THREE.DoubleSide
285
+ }
286
+ )
287
+ ]
288
+ }
289
+ ),
290
+ /* @__PURE__ */ jsxs(
291
+ "mesh",
292
+ {
293
+ position: [0, yOffset, -thickness],
294
+ castShadow,
295
+ receiveShadow,
296
+ customDepthMaterial: depthMaterial,
297
+ children: [
298
+ /* @__PURE__ */ jsx("planeGeometry", { args: [width, height] }),
299
+ /* @__PURE__ */ jsx(
300
+ "meshStandardMaterial",
301
+ {
302
+ map: frontTexture,
303
+ transparent: true,
304
+ alphaTest: 0.38,
305
+ roughness: 0.9,
306
+ metalness: 0.02,
307
+ side: THREE.DoubleSide
308
+ }
309
+ )
310
+ ]
311
+ }
312
+ )
313
+ ] })
314
+ ] });
315
+ };
316
+ var PopUpHinge3D = ({
317
+ foldProgress,
318
+ position = [0, 0, 0],
319
+ foldDirection = 1,
320
+ children
321
+ }) => {
322
+ const clamped = Math.max(0, Math.min(1.05, foldProgress));
323
+ const theta = (1 - Math.min(1, clamped)) * (Math.PI * 0.46) * foldDirection;
324
+ const c = Math.cos(theta);
325
+ const s = Math.sin(theta);
326
+ const posX = position[0];
327
+ const posY = position[1];
328
+ const posZ = position[2];
329
+ const foldMatrix = useMemo(() => {
330
+ const m = new THREE.Matrix4();
331
+ m.set(
332
+ 1,
333
+ 0,
334
+ 0,
335
+ posX,
336
+ 0,
337
+ c,
338
+ 0,
339
+ posY,
340
+ 0,
341
+ -s,
342
+ 1,
343
+ posZ,
344
+ 0,
345
+ 0,
346
+ 0,
347
+ 1
348
+ );
349
+ return m;
350
+ }, [c, s, posX, posY, posZ]);
351
+ if (clamped <= 0.01) return null;
352
+ return /* @__PURE__ */ jsx("group", { matrixAutoUpdate: false, matrix: foldMatrix, children });
353
+ };
354
+
355
+ // packages/kits/src/paper-diorama-3d/ImageCutout3D.tsx
356
+ import { useCallback, useEffect as useEffect2, useState } from "react";
357
+ import { continueRender, delayRender } from "remotion";
358
+ import { hashToHex as hashToHex2 } from "@make-pictures/core";
359
+ import { jsx as jsx2 } from "react/jsx-runtime";
360
+ var IMAGE_ELEMENT_CACHE = /* @__PURE__ */ new Map();
361
+ var IMAGE_LOAD_TIMEOUT_MS = 6e3;
362
+ var ImageCutout3D = ({
363
+ src,
364
+ cacheKey,
365
+ width,
366
+ height,
367
+ pixelWidth = 512,
368
+ pixelHeight = 512,
369
+ whiteBorderPx = 14,
370
+ paddingPx,
371
+ objectFit = "contain",
372
+ position = [0, 0, 0],
373
+ rotation = [0, 0, 0],
374
+ scale = 1,
375
+ anchor = "bottom",
376
+ thickness = 0.038,
377
+ castShadow = true,
378
+ receiveShadow = true,
379
+ emissive = "#000000",
380
+ emissiveIntensity = 0,
381
+ opacity = 1
382
+ }) => {
383
+ const safePadding = paddingPx ?? Math.max(24, whiteBorderPx + 14);
384
+ const resolvedCacheKey = cacheKey ?? `img-cutout-${hashToHex2(src)}`;
385
+ const [loadedImage, setLoadedImage] = useState(
386
+ () => IMAGE_ELEMENT_CACHE.get(src) ?? null
387
+ );
388
+ const [loadFailed, setLoadFailed] = useState(false);
389
+ const [delayHandle] = useState(() => {
390
+ if (typeof Image === "undefined" || IMAGE_ELEMENT_CACHE.has(src)) {
391
+ return null;
392
+ }
393
+ try {
394
+ return delayRender(`Loading ImageCutout3D (${src.slice(0, 64)})`);
395
+ } catch {
396
+ return null;
397
+ }
398
+ });
399
+ useEffect2(() => {
400
+ let released = false;
401
+ const release = () => {
402
+ if (!released && delayHandle !== null) {
403
+ released = true;
404
+ try {
405
+ continueRender(delayHandle);
406
+ } catch {
407
+ }
408
+ }
409
+ };
410
+ const cached = IMAGE_ELEMENT_CACHE.get(src);
411
+ if (cached) {
412
+ setLoadedImage(cached);
413
+ setLoadFailed(false);
414
+ release();
415
+ return;
416
+ }
417
+ if (typeof Image === "undefined") {
418
+ release();
419
+ return;
420
+ }
421
+ let cancelled = false;
422
+ const img = new Image();
423
+ img.crossOrigin = "anonymous";
424
+ img.decoding = "async";
425
+ const timeout = setTimeout(() => {
426
+ if (cancelled) return;
427
+ setLoadFailed(true);
428
+ release();
429
+ }, IMAGE_LOAD_TIMEOUT_MS);
430
+ img.onload = () => {
431
+ clearTimeout(timeout);
432
+ IMAGE_ELEMENT_CACHE.set(src, img);
433
+ if (!cancelled) {
434
+ setLoadedImage(img);
435
+ setLoadFailed(false);
436
+ }
437
+ release();
438
+ };
439
+ img.onerror = () => {
440
+ clearTimeout(timeout);
441
+ if (!cancelled) {
442
+ setLoadFailed(true);
443
+ }
444
+ release();
445
+ };
446
+ img.src = src;
447
+ return () => {
448
+ cancelled = true;
449
+ clearTimeout(timeout);
450
+ release();
451
+ };
452
+ }, [src, delayHandle]);
453
+ const drawImageCutout = useCallback(
454
+ (ctx, w, h) => {
455
+ const innerW = Math.max(16, w - safePadding * 2);
456
+ const innerH = Math.max(16, h - safePadding * 2);
457
+ if (loadedImage && loadedImage.naturalWidth > 0 && loadedImage.naturalHeight > 0) {
458
+ const natW = loadedImage.naturalWidth;
459
+ const natH = loadedImage.naturalHeight;
460
+ let drawW = innerW;
461
+ let drawH = innerH;
462
+ let drawX = safePadding;
463
+ let drawY = safePadding;
464
+ if (objectFit === "contain") {
465
+ const fitScale = Math.min(innerW / natW, innerH / natH);
466
+ drawW = natW * fitScale;
467
+ drawH = natH * fitScale;
468
+ drawX = (w - drawW) / 2;
469
+ drawY = anchor === "bottom" ? h - safePadding - drawH : (h - drawH) / 2;
470
+ } else if (objectFit === "cover") {
471
+ const coverScale = Math.max(innerW / natW, innerH / natH);
472
+ drawW = natW * coverScale;
473
+ drawH = natH * coverScale;
474
+ drawX = (w - drawW) / 2;
475
+ drawY = (h - drawH) / 2;
476
+ ctx.save();
477
+ ctx.beginPath();
478
+ ctx.rect(safePadding, safePadding, innerW, innerH);
479
+ ctx.clip();
480
+ ctx.drawImage(loadedImage, drawX, drawY, drawW, drawH);
481
+ ctx.restore();
482
+ return;
483
+ }
484
+ ctx.drawImage(loadedImage, drawX, drawY, drawW, drawH);
485
+ return;
486
+ }
487
+ if (loadFailed) {
488
+ ctx.fillStyle = "#F1F5F9";
489
+ ctx.strokeStyle = "#94A3B8";
490
+ ctx.lineWidth = 6;
491
+ ctx.beginPath();
492
+ ctx.roundRect(safePadding, safePadding, innerW, innerH, 24);
493
+ ctx.fill();
494
+ ctx.stroke();
495
+ }
496
+ },
497
+ [loadedImage, loadFailed, safePadding, objectFit, anchor]
498
+ );
499
+ if (!loadedImage && !loadFailed) {
500
+ return null;
501
+ }
502
+ return /* @__PURE__ */ jsx2(
503
+ PaperCutout3D,
504
+ {
505
+ cacheKey: resolvedCacheKey,
506
+ cacheDeps: [
507
+ src,
508
+ loadedImage ? 1 : 0,
509
+ loadFailed ? 1 : 0,
510
+ safePadding,
511
+ objectFit,
512
+ anchor
513
+ ],
514
+ pixelWidth,
515
+ pixelHeight,
516
+ width,
517
+ height,
518
+ draw: drawImageCutout,
519
+ whiteBorderPx,
520
+ position,
521
+ rotation,
522
+ scale,
523
+ anchor,
524
+ thickness,
525
+ castShadow,
526
+ receiveShadow,
527
+ emissive,
528
+ emissiveIntensity,
529
+ opacity
530
+ }
531
+ );
532
+ };
533
+
534
+ // packages/kits/src/paper-diorama-3d/PaperPopUpBook3D.tsx
535
+ import { useMemo as useMemo2 } from "react";
536
+ import * as THREE2 from "three";
537
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
538
+ var MAX_FLOOR_TEXTURES = 8;
539
+ var FLOOR_TEX_CACHE = /* @__PURE__ */ new Map();
540
+ function getFloorTexture(theme) {
541
+ const existing = FLOOR_TEX_CACHE.get(theme);
542
+ if (existing) {
543
+ FLOOR_TEX_CACHE.delete(theme);
544
+ FLOOR_TEX_CACHE.set(theme, existing);
545
+ return existing;
546
+ }
547
+ const canvas = document.createElement("canvas");
548
+ canvas.width = 1024;
549
+ canvas.height = 1024;
550
+ const ctx = canvas.getContext("2d");
551
+ if (theme === "mint-plains") {
552
+ const grad = ctx.createLinearGradient(0, 0, 0, 1024);
553
+ grad.addColorStop(0, "#A7F3D0");
554
+ grad.addColorStop(0.55, "#6EE7B7");
555
+ grad.addColorStop(1, "#A7F3D0");
556
+ ctx.fillStyle = grad;
557
+ ctx.fillRect(0, 0, 1024, 1024);
558
+ ctx.fillStyle = "#FDE68A";
559
+ ctx.beginPath();
560
+ ctx.moveTo(410, 0);
561
+ ctx.lineTo(614, 0);
562
+ ctx.lineTo(760, 1024);
563
+ ctx.lineTo(264, 1024);
564
+ ctx.closePath();
565
+ ctx.fill();
566
+ ctx.strokeStyle = "#F59E0B";
567
+ ctx.lineWidth = 6;
568
+ ctx.setLineDash([18, 14]);
569
+ ctx.stroke();
570
+ ctx.setLineDash([]);
571
+ } else if (theme === "checkerboard-tile") {
572
+ ctx.fillStyle = "#FFFBEB";
573
+ ctx.fillRect(0, 0, 1024, 1024);
574
+ const cols = 12;
575
+ const rows = 12;
576
+ const cw = 1024 / cols;
577
+ const rh = 1024 / rows;
578
+ for (let r = 0; r < rows; r++) {
579
+ for (let c = 0; c < cols; c++) {
580
+ if ((r + c) % 2 === 0) {
581
+ ctx.fillStyle = "#334155";
582
+ ctx.fillRect(c * cw, r * rh, cw, rh);
583
+ } else {
584
+ ctx.fillStyle = "#FEF3C7";
585
+ ctx.fillRect(c * cw, r * rh, cw, rh);
586
+ }
587
+ }
588
+ }
589
+ } else if (theme === "warm-parquet") {
590
+ ctx.fillStyle = "#B45309";
591
+ ctx.fillRect(0, 0, 1024, 1024);
592
+ for (let r = 0; r < 16; r++) {
593
+ ctx.fillStyle = r % 2 === 0 ? "#92400E" : "#B45309";
594
+ ctx.fillRect(0, r * 64, 1024, 62);
595
+ ctx.strokeStyle = "#78350F";
596
+ ctx.lineWidth = 3;
597
+ ctx.beginPath();
598
+ ctx.moveTo(0, r * 64);
599
+ ctx.lineTo(1024, r * 64);
600
+ ctx.stroke();
601
+ }
602
+ ctx.fillStyle = "#881337";
603
+ ctx.fillRect(140, 180, 744, 660);
604
+ ctx.strokeStyle = "#FBBF24";
605
+ ctx.lineWidth = 14;
606
+ ctx.strokeRect(164, 204, 696, 612);
607
+ } else {
608
+ const grad = ctx.createLinearGradient(0, 0, 0, 1024);
609
+ grad.addColorStop(0, "#A7F3D0");
610
+ grad.addColorStop(0.62, "#6EE7B7");
611
+ grad.addColorStop(0.63, "#D97706");
612
+ grad.addColorStop(1, "#92400E");
613
+ ctx.fillStyle = grad;
614
+ ctx.fillRect(0, 0, 1024, 1024);
615
+ }
616
+ ctx.strokeStyle = "#FFFDF7";
617
+ ctx.lineWidth = 28;
618
+ ctx.strokeRect(14, 14, 996, 996);
619
+ ctx.strokeStyle = "#D6C4A8";
620
+ ctx.lineWidth = 6;
621
+ ctx.strokeRect(28, 28, 968, 968);
622
+ const tex = new THREE2.CanvasTexture(canvas);
623
+ tex.colorSpace = THREE2.SRGBColorSpace;
624
+ tex.anisotropy = 8;
625
+ tex.needsUpdate = true;
626
+ if (FLOOR_TEX_CACHE.size >= MAX_FLOOR_TEXTURES) {
627
+ const oldestKey = FLOOR_TEX_CACHE.keys().next().value;
628
+ if (oldestKey !== void 0) {
629
+ const oldestTex = FLOOR_TEX_CACHE.get(oldestKey);
630
+ FLOOR_TEX_CACHE.delete(oldestKey);
631
+ oldestTex?.dispose();
632
+ }
633
+ }
634
+ FLOOR_TEX_CACHE.set(theme, tex);
635
+ return tex;
636
+ }
637
+ var PaperBookPageTurn3D = ({
638
+ pageTurnProgress,
639
+ nextFloorTheme,
640
+ spreadWidth = 23.4,
641
+ spreadDepth = 15.4
642
+ }) => {
643
+ const nextFloorTex = useMemo2(
644
+ () => getFloorTexture(nextFloorTheme),
645
+ [nextFloorTheme]
646
+ );
647
+ const isTurning = pageTurnProgress > 0.01 && pageTurnProgress < 0.99;
648
+ if (!isTurning) {
649
+ return null;
650
+ }
651
+ const halfW = spreadWidth / 2;
652
+ const smoothP = 0.5 - 0.5 * Math.cos(pageTurnProgress * Math.PI);
653
+ const rollX = halfW - smoothP * spreadWidth;
654
+ const unrolledWidth = Math.max(0.05, halfW - rollX);
655
+ const unrolledCenterX = rollX + unrolledWidth / 2;
656
+ return /* @__PURE__ */ jsxs2("group", { children: [
657
+ /* @__PURE__ */ jsxs2(
658
+ "mesh",
659
+ {
660
+ position: [unrolledCenterX, 0.015, -0.5],
661
+ rotation: [-Math.PI / 2, 0, 0],
662
+ scale: [unrolledWidth, spreadDepth, 1],
663
+ receiveShadow: true,
664
+ children: [
665
+ /* @__PURE__ */ jsx3("planeGeometry", { args: [1, 1] }),
666
+ /* @__PURE__ */ jsx3(
667
+ "meshStandardMaterial",
668
+ {
669
+ map: nextFloorTex,
670
+ roughness: 0.88,
671
+ metalness: 0.02
672
+ }
673
+ )
674
+ ]
675
+ }
676
+ ),
677
+ /* @__PURE__ */ jsxs2(
678
+ "mesh",
679
+ {
680
+ position: [rollX, 0.024, -0.5],
681
+ scale: [0.18, 0.018, spreadDepth + 0.02],
682
+ receiveShadow: true,
683
+ children: [
684
+ /* @__PURE__ */ jsx3("boxGeometry", { args: [1, 1, 1] }),
685
+ /* @__PURE__ */ jsx3(
686
+ "meshStandardMaterial",
687
+ {
688
+ color: "#FFFDF7",
689
+ roughness: 0.85,
690
+ metalness: 0.02
691
+ }
692
+ )
693
+ ]
694
+ }
695
+ ),
696
+ /* @__PURE__ */ jsxs2(
697
+ "mesh",
698
+ {
699
+ position: [rollX + 0.12, 0.026, -0.5],
700
+ scale: [0.06, 0.018, spreadDepth - 0.02],
701
+ children: [
702
+ /* @__PURE__ */ jsx3("boxGeometry", { args: [1, 1, 1] }),
703
+ /* @__PURE__ */ jsx3(
704
+ "meshStandardMaterial",
705
+ {
706
+ color: "#FBBF24",
707
+ roughness: 0.4,
708
+ metalness: 0.45
709
+ }
710
+ )
711
+ ]
712
+ }
713
+ )
714
+ ] });
715
+ };
716
+ var PopUpBookBase3D = ({ floorTheme, nextFloorTheme, pageTurnProgress = 0 }) => {
717
+ const floorTex = useMemo2(() => getFloorTexture(floorTheme), [floorTheme]);
718
+ return /* @__PURE__ */ jsxs2("group", { children: [
719
+ /* @__PURE__ */ jsxs2(
720
+ "mesh",
721
+ {
722
+ position: [0, -0.42, 0],
723
+ rotation: [-Math.PI / 2, 0, 0],
724
+ receiveShadow: true,
725
+ children: [
726
+ /* @__PURE__ */ jsx3("planeGeometry", { args: [64, 48] }),
727
+ /* @__PURE__ */ jsx3("meshStandardMaterial", { color: "#3B1D0A", roughness: 0.92 })
728
+ ]
729
+ }
730
+ ),
731
+ /* @__PURE__ */ jsxs2("mesh", { position: [0, -0.22, -0.5], receiveShadow: true, castShadow: true, children: [
732
+ /* @__PURE__ */ jsx3("boxGeometry", { args: [24.6, 0.18, 16.6] }),
733
+ /* @__PURE__ */ jsx3("meshStandardMaterial", { color: "#7C2D12", roughness: 0.8, metalness: 0.08 })
734
+ ] }),
735
+ [
736
+ [-11.9, -8],
737
+ [11.9, -8],
738
+ [-11.9, 7],
739
+ [11.9, 7]
740
+ ].map(([cx, cz], i) => /* @__PURE__ */ jsxs2("mesh", { position: [cx, -0.12, cz], children: [
741
+ /* @__PURE__ */ jsx3("boxGeometry", { args: [0.8, 0.06, 0.8] }),
742
+ /* @__PURE__ */ jsx3(
743
+ "meshStandardMaterial",
744
+ {
745
+ color: "#FBBF24",
746
+ roughness: 0.35,
747
+ metalness: 0.65
748
+ }
749
+ )
750
+ ] }, i)),
751
+ /* @__PURE__ */ jsxs2("mesh", { position: [0, -0.08, -0.5], receiveShadow: true, castShadow: true, children: [
752
+ /* @__PURE__ */ jsx3("boxGeometry", { args: [23.6, 0.16, 15.6] }),
753
+ /* @__PURE__ */ jsx3("meshStandardMaterial", { color: "#F5EFE6", roughness: 0.9 })
754
+ ] }),
755
+ /* @__PURE__ */ jsxs2(
756
+ "mesh",
757
+ {
758
+ position: [0, 5e-3, -0.5],
759
+ rotation: [-Math.PI / 2, 0, 0],
760
+ receiveShadow: true,
761
+ children: [
762
+ /* @__PURE__ */ jsx3("planeGeometry", { args: [23.4, 15.4] }),
763
+ /* @__PURE__ */ jsx3(
764
+ "meshStandardMaterial",
765
+ {
766
+ map: floorTex,
767
+ roughness: 0.88,
768
+ metalness: 0.02
769
+ }
770
+ )
771
+ ]
772
+ }
773
+ ),
774
+ /* @__PURE__ */ jsx3(
775
+ PaperBookPageTurn3D,
776
+ {
777
+ pageTurnProgress,
778
+ nextFloorTheme: nextFloorTheme ?? floorTheme
779
+ }
780
+ )
781
+ ] });
782
+ };
783
+ var PaperCloudCutout3D = ({ position, scale = 1, tint = "#FCE7F3", idSuffix = "pink" }) => /* @__PURE__ */ jsx3(
784
+ PaperCutout3D,
785
+ {
786
+ cacheKey: `paper-cloud-${idSuffix}`,
787
+ cacheDeps: [tint],
788
+ width: 2.6 * scale,
789
+ height: 1.3 * scale,
790
+ pixelWidth: 512,
791
+ pixelHeight: 256,
792
+ whiteBorderPx: 16,
793
+ position,
794
+ anchor: "center",
795
+ thickness: 0.045,
796
+ draw: (ctx) => {
797
+ ctx.fillStyle = tint;
798
+ ctx.strokeStyle = "#F472B6";
799
+ ctx.lineWidth = 6;
800
+ ctx.beginPath();
801
+ ctx.moveTo(90, 195);
802
+ ctx.lineTo(422, 195);
803
+ ctx.bezierCurveTo(468, 195, 472, 130, 420, 122);
804
+ ctx.bezierCurveTo(412, 65, 340, 48, 300, 82);
805
+ ctx.bezierCurveTo(272, 22, 178, 26, 156, 88);
806
+ ctx.bezierCurveTo(108, 74, 62, 112, 76, 152);
807
+ ctx.bezierCurveTo(42, 158, 48, 195, 90, 195);
808
+ ctx.closePath();
809
+ ctx.fill();
810
+ ctx.stroke();
811
+ ctx.fillStyle = "rgba(244, 114, 182, 0.18)";
812
+ ctx.beginPath();
813
+ ctx.ellipse(256, 172, 145, 16, 0, 0, Math.PI * 2);
814
+ ctx.fill();
815
+ }
816
+ }
817
+ );
818
+ var PaperPineTree3D = ({ position, scale = 1 }) => /* @__PURE__ */ jsx3(
819
+ PaperCutout3D,
820
+ {
821
+ cacheKey: "paper-pine-tree",
822
+ width: 1.35 * scale,
823
+ height: 2.1 * scale,
824
+ pixelWidth: 256,
825
+ pixelHeight: 384,
826
+ whiteBorderPx: 14,
827
+ position,
828
+ anchor: "bottom",
829
+ thickness: 0.04,
830
+ draw: (ctx) => {
831
+ ctx.fillStyle = "#78350F";
832
+ ctx.fillRect(108, 285, 40, 75);
833
+ const tiers = [
834
+ { yTop: 155, yBot: 295, halfW: 92, color: "#059669" },
835
+ { yTop: 92, yBot: 215, halfW: 76, color: "#10B981" },
836
+ { yTop: 36, yBot: 145, halfW: 56, color: "#34D399" }
837
+ ];
838
+ for (const t of tiers) {
839
+ ctx.fillStyle = t.color;
840
+ ctx.strokeStyle = "#065F46";
841
+ ctx.lineWidth = 5;
842
+ ctx.beginPath();
843
+ ctx.moveTo(128, t.yTop);
844
+ ctx.lineTo(128 + t.halfW, t.yBot);
845
+ ctx.lineTo(128 - t.halfW, t.yBot);
846
+ ctx.closePath();
847
+ ctx.fill();
848
+ ctx.stroke();
849
+ }
850
+ }
851
+ }
852
+ );
853
+ var PaperMountainBackdrop3D = ({ position = [0, 0, -5.2], scale = 1 }) => {
854
+ return /* @__PURE__ */ jsxs2("group", { position, scale, children: [
855
+ /* @__PURE__ */ jsx3(
856
+ PaperCutout3D,
857
+ {
858
+ cacheKey: "paper-mountain-west",
859
+ width: 4.6,
860
+ height: 3.8,
861
+ pixelWidth: 512,
862
+ pixelHeight: 420,
863
+ whiteBorderPx: 14,
864
+ position: [-3.2, 0, -0.9],
865
+ thickness: 0.05,
866
+ draw: (ctx) => {
867
+ ctx.fillStyle = "#C4B5FD";
868
+ ctx.strokeStyle = "#7C3AED";
869
+ ctx.lineWidth = 6;
870
+ ctx.beginPath();
871
+ ctx.moveTo(256, 36);
872
+ ctx.lineTo(480, 396);
873
+ ctx.lineTo(32, 396);
874
+ ctx.closePath();
875
+ ctx.fill();
876
+ ctx.stroke();
877
+ ctx.fillStyle = "#F5F3FF";
878
+ ctx.beginPath();
879
+ ctx.moveTo(256, 36);
880
+ ctx.lineTo(340, 185);
881
+ ctx.lineTo(276, 155);
882
+ ctx.lineTo(234, 195);
883
+ ctx.lineTo(172, 185);
884
+ ctx.closePath();
885
+ ctx.fill();
886
+ }
887
+ }
888
+ ),
889
+ /* @__PURE__ */ jsx3(
890
+ PaperCutout3D,
891
+ {
892
+ cacheKey: "paper-mountain-east",
893
+ width: 4.8,
894
+ height: 4,
895
+ pixelWidth: 512,
896
+ pixelHeight: 420,
897
+ whiteBorderPx: 14,
898
+ position: [3.4, 0, -0.8],
899
+ thickness: 0.05,
900
+ draw: (ctx) => {
901
+ ctx.fillStyle = "#A5B4FC";
902
+ ctx.strokeStyle = "#4F46E5";
903
+ ctx.lineWidth = 6;
904
+ ctx.beginPath();
905
+ ctx.moveTo(256, 30);
906
+ ctx.lineTo(484, 396);
907
+ ctx.lineTo(28, 396);
908
+ ctx.closePath();
909
+ ctx.fill();
910
+ ctx.stroke();
911
+ ctx.fillStyle = "#EEF2FF";
912
+ ctx.beginPath();
913
+ ctx.moveTo(256, 30);
914
+ ctx.lineTo(348, 180);
915
+ ctx.lineTo(282, 152);
916
+ ctx.lineTo(222, 188);
917
+ ctx.lineTo(164, 178);
918
+ ctx.closePath();
919
+ ctx.fill();
920
+ }
921
+ }
922
+ ),
923
+ /* @__PURE__ */ jsx3(
924
+ PaperCutout3D,
925
+ {
926
+ cacheKey: "paper-mountain-center",
927
+ width: 5.4,
928
+ height: 4.8,
929
+ pixelWidth: 560,
930
+ pixelHeight: 500,
931
+ whiteBorderPx: 16,
932
+ position: [0, 0, 0],
933
+ thickness: 0.055,
934
+ draw: (ctx) => {
935
+ ctx.fillStyle = "#60A5FA";
936
+ ctx.strokeStyle = "#1D4ED8";
937
+ ctx.lineWidth = 7;
938
+ ctx.beginPath();
939
+ ctx.moveTo(280, 28);
940
+ ctx.lineTo(528, 476);
941
+ ctx.lineTo(32, 476);
942
+ ctx.closePath();
943
+ ctx.fill();
944
+ ctx.stroke();
945
+ ctx.fillStyle = "#FFFFFF";
946
+ ctx.beginPath();
947
+ ctx.moveTo(280, 28);
948
+ ctx.lineTo(384, 216);
949
+ ctx.lineTo(318, 178);
950
+ ctx.lineTo(280, 224);
951
+ ctx.lineTo(236, 182);
952
+ ctx.lineTo(176, 216);
953
+ ctx.closePath();
954
+ ctx.fill();
955
+ }
956
+ }
957
+ )
958
+ ] });
959
+ };
960
+
961
+ // packages/kits/src/paper-diorama-3d/PaperStageLighting3D.tsx
962
+ import { useLayoutEffect } from "react";
963
+ import { useThree } from "@react-three/fiber";
964
+ import * as THREE3 from "three";
965
+ import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
966
+ var PaperCameraRig3D = ({
967
+ cameraPos,
968
+ lookAt,
969
+ fov = 34
970
+ }) => {
971
+ const camera = useThree((state) => state.camera);
972
+ const [camX, camY, camZ] = cameraPos;
973
+ const [lookX, lookY, lookZ] = lookAt;
974
+ useLayoutEffect(() => {
975
+ camera.position.set(camX, camY, camZ);
976
+ if (camera instanceof THREE3.PerspectiveCamera) {
977
+ camera.fov = fov;
978
+ }
979
+ camera.lookAt(lookX, lookY, lookZ);
980
+ camera.updateProjectionMatrix();
981
+ }, [camera, camX, camY, camZ, lookX, lookY, lookZ, fov]);
982
+ return null;
983
+ };
984
+ var PaperStageLighting3D = ({
985
+ preset = "daylight",
986
+ sunPosition
987
+ }) => {
988
+ const gl = useThree((state) => state.gl);
989
+ useLayoutEffect(() => {
990
+ gl.shadowMap.enabled = true;
991
+ gl.shadowMap.type = THREE3.PCFSoftShadowMap;
992
+ gl.toneMapping = THREE3.ACESFilmicToneMapping;
993
+ gl.toneMappingExposure = 1.06;
994
+ }, [gl]);
995
+ let sunColor = "#FFF5E4";
996
+ let sunIntensity = 1.95;
997
+ let ambientIntensity = 0.68;
998
+ const sunPos = sunPosition ?? [-7.5, 11.5, 8.5];
999
+ if (preset === "golden-hour") {
1000
+ sunColor = "#FDE68A";
1001
+ sunIntensity = 1.85;
1002
+ ambientIntensity = 0.58;
1003
+ } else if (preset === "indoor-study") {
1004
+ sunColor = "#FEF3C7";
1005
+ sunIntensity = 1.65;
1006
+ ambientIntensity = 0.56;
1007
+ } else if (preset === "spotlight") {
1008
+ sunColor = "#F8FAFC";
1009
+ sunIntensity = 1.35;
1010
+ ambientIntensity = 0.45;
1011
+ }
1012
+ return /* @__PURE__ */ jsxs3(Fragment2, { children: [
1013
+ /* @__PURE__ */ jsx4("hemisphereLight", { args: ["#FFFDF9", "#92400E", ambientIntensity] }),
1014
+ /* @__PURE__ */ jsx4(
1015
+ "directionalLight",
1016
+ {
1017
+ position: sunPos,
1018
+ intensity: sunIntensity,
1019
+ color: sunColor,
1020
+ castShadow: true,
1021
+ "shadow-mapSize-width": 2048,
1022
+ "shadow-mapSize-height": 2048,
1023
+ "shadow-camera-near": 1,
1024
+ "shadow-camera-far": 38,
1025
+ "shadow-camera-left": -14,
1026
+ "shadow-camera-right": 14,
1027
+ "shadow-camera-top": 12,
1028
+ "shadow-camera-bottom": -10,
1029
+ "shadow-bias": -5e-4
1030
+ }
1031
+ ),
1032
+ /* @__PURE__ */ jsx4(
1033
+ "directionalLight",
1034
+ {
1035
+ position: [9, 6, -4],
1036
+ intensity: 0.55,
1037
+ color: "#FDE68A"
1038
+ }
1039
+ )
1040
+ ] });
1041
+ };
1042
+
1043
+ export {
1044
+ prunePaperTextureCache,
1045
+ getPaperTextureCacheStats,
1046
+ serializePaperCutoutDeps,
1047
+ buildPaperCutoutCompositeKey,
1048
+ PaperCutout3D,
1049
+ PopUpHinge3D,
1050
+ ImageCutout3D,
1051
+ getFloorTexture,
1052
+ PaperBookPageTurn3D,
1053
+ PopUpBookBase3D,
1054
+ PaperCloudCutout3D,
1055
+ PaperPineTree3D,
1056
+ PaperMountainBackdrop3D,
1057
+ PaperCameraRig3D,
1058
+ PaperStageLighting3D
1059
+ };