@pascal-app/viewer 1.0.3 → 1.0.5
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/dist/capture/capture-runtime.js +1 -1
- package/dist/components/error-boundary.d.ts.map +1 -1
- package/dist/components/renderers/node-renderer.d.ts +1 -1
- package/dist/components/renderers/node-renderer.d.ts.map +1 -1
- package/dist/components/renderers/node-renderer.js +12 -4
- package/dist/components/renderers/parametric-node-renderer.d.ts.map +1 -1
- package/dist/components/viewer/bvh-ecctrl.d.ts.map +1 -1
- package/dist/components/viewer/frame-limiter.d.ts +1 -0
- package/dist/components/viewer/frame-limiter.d.ts.map +1 -1
- package/dist/components/viewer/frame-limiter.js +22 -3
- package/dist/components/viewer/glb-interactive.d.ts +25 -1
- package/dist/components/viewer/glb-interactive.d.ts.map +1 -1
- package/dist/components/viewer/glb-interactive.js +253 -86
- package/dist/components/viewer/glb-mechanisms.d.ts +10 -0
- package/dist/components/viewer/glb-mechanisms.d.ts.map +1 -0
- package/dist/components/viewer/glb-mechanisms.js +19 -0
- package/dist/components/viewer/glb-reference-nodes.js +1 -1
- package/dist/components/viewer/glb-replace-instances.js +1 -1
- package/dist/components/viewer/glb-scene.d.ts +45 -27
- package/dist/components/viewer/glb-scene.d.ts.map +1 -1
- package/dist/components/viewer/glb-scene.js +421 -508
- package/dist/components/viewer/glb-walkthrough-controller.d.ts +1 -1
- package/dist/components/viewer/glb-walkthrough-controller.d.ts.map +1 -1
- package/dist/components/viewer/glb-walkthrough-controller.js +13 -2
- package/dist/components/viewer/index.d.ts +24 -1
- package/dist/components/viewer/index.d.ts.map +1 -1
- package/dist/components/viewer/index.js +111 -37
- package/dist/components/viewer/lights.d.ts.map +1 -1
- package/dist/components/viewer/lights.js +113 -22
- package/dist/components/viewer/perf-monitor.d.ts.map +1 -1
- package/dist/components/viewer/perf-monitor.js +6 -0
- package/dist/components/viewer/perf-panel.d.ts.map +1 -1
- package/dist/components/viewer/perf-panel.js +1 -1
- package/dist/components/viewer/post-processing.d.ts.map +1 -1
- package/dist/components/viewer/post-processing.js +104 -36
- package/dist/components/viewer/registered-systems.d.ts.map +1 -1
- package/dist/components/viewer/registered-systems.js +17 -3
- package/dist/components/viewer/render-error.d.ts +18 -0
- package/dist/components/viewer/render-error.d.ts.map +1 -0
- package/dist/components/viewer/render-error.js +29 -0
- package/dist/components/viewer/scene-bvh.d.ts.map +1 -1
- package/dist/components/viewer/scene-bvh.js +1 -1
- package/dist/components/viewer/scene-environment.d.ts.map +1 -1
- package/dist/components/viewer/scene-environment.js +31 -11
- package/dist/components/viewer/scene-ground-replacement.d.ts +6 -0
- package/dist/components/viewer/scene-ground-replacement.d.ts.map +1 -1
- package/dist/components/viewer/scene-ground-replacement.js +15 -0
- package/dist/components/viewer/selection-manager.d.ts.map +1 -1
- package/dist/components/viewer/selection-manager.js +25 -7
- package/dist/components/viewer/underground-ambiance.d.ts +9 -0
- package/dist/components/viewer/underground-ambiance.d.ts.map +1 -0
- package/dist/components/viewer/underground-ambiance.js +105 -0
- package/dist/components/viewer/viewer-camera.d.ts +4 -0
- package/dist/components/viewer/viewer-camera.d.ts.map +1 -1
- package/dist/components/viewer/viewer-camera.js +12 -2
- package/dist/components/viewer/viewer-presentations.d.ts.map +1 -1
- package/dist/hooks/use-gltf-ktx2.d.ts.map +1 -1
- package/dist/hooks/use-gltf-ktx2.js +2 -0
- package/dist/hooks/use-node-events.d.ts.map +1 -1
- package/dist/index.d.ts +36 -16
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +34 -19
- package/dist/lib/asset-url.d.ts.map +1 -1
- package/dist/lib/asset-url.js +7 -1
- package/dist/lib/backdrop.d.ts +17 -0
- package/dist/lib/backdrop.d.ts.map +1 -1
- package/dist/lib/backdrop.js +18 -1
- package/dist/lib/construction-dust.d.ts +57 -0
- package/dist/lib/construction-dust.d.ts.map +1 -0
- package/dist/lib/construction-dust.js +187 -0
- package/dist/lib/csg-utils.js +1 -1
- package/dist/lib/display-state.d.ts +25 -0
- package/dist/lib/display-state.d.ts.map +1 -0
- package/dist/lib/display-state.js +1 -0
- package/dist/lib/foundation-materials.d.ts +26 -0
- package/dist/lib/foundation-materials.d.ts.map +1 -0
- package/dist/lib/foundation-materials.js +65 -0
- package/dist/lib/gltf-unlit.d.ts +10 -0
- package/dist/lib/gltf-unlit.d.ts.map +1 -0
- package/dist/lib/gltf-unlit.js +23 -0
- package/dist/lib/ground-window.d.ts +33 -0
- package/dist/lib/ground-window.d.ts.map +1 -0
- package/dist/lib/ground-window.js +114 -0
- package/dist/lib/hero-pose.d.ts +13 -3
- package/dist/lib/hero-pose.d.ts.map +1 -1
- package/dist/lib/hero-pose.js +72 -5
- package/dist/lib/ink-edges.d.ts +6 -3
- package/dist/lib/ink-edges.d.ts.map +1 -1
- package/dist/lib/ink-edges.js +17 -7
- package/dist/lib/key-light-override.d.ts +11 -0
- package/dist/lib/key-light-override.d.ts.map +1 -0
- package/dist/lib/key-light-override.js +14 -0
- package/dist/lib/ktx2-loader.d.ts +7 -0
- package/dist/lib/ktx2-loader.d.ts.map +1 -1
- package/dist/lib/ktx2-loader.js +35 -0
- package/dist/lib/layer-pass.d.ts.map +1 -1
- package/dist/lib/layers.d.ts +11 -7
- package/dist/lib/layers.d.ts.map +1 -1
- package/dist/lib/layers.js +16 -6
- package/dist/lib/live-frame-hold.d.ts +14 -0
- package/dist/lib/live-frame-hold.d.ts.map +1 -0
- package/dist/lib/live-frame-hold.js +28 -0
- package/dist/lib/materials.d.ts +19 -7
- package/dist/lib/materials.d.ts.map +1 -1
- package/dist/lib/materials.js +65 -9
- package/dist/lib/merged-outline-node.d.ts +5 -0
- package/dist/lib/merged-outline-node.d.ts.map +1 -1
- package/dist/lib/merged-outline-node.js +80 -2
- package/dist/lib/node-top-surface-height.d.ts +6 -0
- package/dist/lib/node-top-surface-height.d.ts.map +1 -0
- package/dist/lib/node-top-surface-height.js +74 -0
- package/dist/lib/perf-probe-state.d.ts +39 -0
- package/dist/lib/perf-probe-state.d.ts.map +1 -0
- package/dist/lib/perf-probe-state.js +17 -0
- package/dist/lib/pointer-events.d.ts +26 -2
- package/dist/lib/pointer-events.d.ts.map +1 -1
- package/dist/lib/pointer-events.js +99 -12
- package/dist/lib/post-processing-resources.d.ts.map +1 -1
- package/dist/lib/procedural-emission.d.ts +7 -0
- package/dist/lib/procedural-emission.d.ts.map +1 -0
- package/dist/lib/procedural-emission.js +54 -0
- package/dist/lib/reveal-candidates.d.ts +80 -0
- package/dist/lib/reveal-candidates.d.ts.map +1 -0
- package/dist/lib/reveal-candidates.js +524 -0
- package/dist/lib/reveal-ghost.d.ts +9 -0
- package/dist/lib/reveal-ghost.d.ts.map +1 -0
- package/dist/lib/reveal-ghost.js +62 -0
- package/dist/lib/reveal-pending.d.ts +12 -0
- package/dist/lib/reveal-pending.d.ts.map +1 -0
- package/dist/lib/reveal-pending.js +12 -0
- package/dist/lib/reveal-pose.d.ts +70 -0
- package/dist/lib/reveal-pose.d.ts.map +1 -0
- package/dist/lib/reveal-pose.js +248 -0
- package/dist/lib/reveal-schedule.d.ts +208 -0
- package/dist/lib/reveal-schedule.d.ts.map +1 -0
- package/dist/lib/reveal-schedule.js +410 -0
- package/dist/lib/reveal-texture-scale.d.ts +5 -0
- package/dist/lib/reveal-texture-scale.d.ts.map +1 -0
- package/dist/lib/reveal-texture-scale.js +113 -0
- package/dist/lib/reveal-windows.d.ts +23 -0
- package/dist/lib/reveal-windows.d.ts.map +1 -0
- package/dist/lib/reveal-windows.js +31 -0
- package/dist/lib/reveal-wipe.d.ts +71 -0
- package/dist/lib/reveal-wipe.d.ts.map +1 -0
- package/dist/lib/reveal-wipe.js +105 -0
- package/dist/lib/scene-pass-mrt.d.ts +12 -0
- package/dist/lib/scene-pass-mrt.d.ts.map +1 -0
- package/dist/lib/scene-pass-mrt.js +23 -0
- package/dist/lib/scene-support-height.d.ts +4 -0
- package/dist/lib/scene-support-height.d.ts.map +1 -0
- package/dist/lib/scene-support-height.js +34 -0
- package/dist/lib/scene-themes.d.ts +30 -8
- package/dist/lib/scene-themes.d.ts.map +1 -1
- package/dist/lib/scene-themes.js +36 -0
- package/dist/lib/scene-visibility.d.ts +17 -1
- package/dist/lib/scene-visibility.d.ts.map +1 -1
- package/dist/lib/scene-visibility.js +53 -3
- package/dist/lib/scripted-opening.d.ts +11 -0
- package/dist/lib/scripted-opening.d.ts.map +1 -0
- package/dist/lib/scripted-opening.js +20 -0
- package/dist/lib/site-surfaces.d.ts +32 -0
- package/dist/lib/site-surfaces.d.ts.map +1 -0
- package/dist/lib/site-surfaces.js +70 -0
- package/dist/lib/snapshot-pipeline.d.ts +34 -3
- package/dist/lib/snapshot-pipeline.d.ts.map +1 -1
- package/dist/lib/snapshot-pipeline.js +247 -117
- package/dist/lib/terrain-perimeter-fill.d.ts.map +1 -1
- package/dist/lib/terrain-perimeter-fill.js +22 -14
- package/dist/lib/tsl-compat.d.ts +2 -2
- package/dist/lib/tsl-compat.d.ts.map +1 -1
- package/dist/lib/underground-ambiance.d.ts +168 -0
- package/dist/lib/underground-ambiance.d.ts.map +1 -0
- package/dist/lib/underground-ambiance.js +334 -0
- package/dist/lib/zone-geometry.d.ts +11 -0
- package/dist/lib/zone-geometry.d.ts.map +1 -0
- package/dist/lib/zone-geometry.js +46 -0
- package/dist/store/use-item-light-pool.d.ts +14 -10
- package/dist/store/use-item-light-pool.d.ts.map +1 -1
- package/dist/store/use-item-light-pool.js +52 -26
- package/dist/store/use-viewer.d.ts +194 -26
- package/dist/store/use-viewer.d.ts.map +1 -1
- package/dist/store/use-viewer.js +164 -21
- package/dist/systems/ceiling/ceiling-system.d.ts +15 -1
- package/dist/systems/ceiling/ceiling-system.d.ts.map +1 -1
- package/dist/systems/ceiling/ceiling-system.js +88 -6
- package/dist/systems/construction-reveal/construction-dust.d.ts +16 -0
- package/dist/systems/construction-reveal/construction-dust.d.ts.map +1 -0
- package/dist/systems/construction-reveal/construction-dust.js +92 -0
- package/dist/systems/construction-reveal/construction-reveal.d.ts +276 -0
- package/dist/systems/construction-reveal/construction-reveal.d.ts.map +1 -0
- package/dist/systems/construction-reveal/construction-reveal.js +2383 -0
- package/dist/systems/construction-reveal/dust-material.d.ts +10 -0
- package/dist/systems/construction-reveal/dust-material.d.ts.map +1 -0
- package/dist/systems/construction-reveal/dust-material.js +17 -0
- package/dist/systems/construction-reveal/ghost-dress.d.ts +10 -0
- package/dist/systems/construction-reveal/ghost-dress.d.ts.map +1 -0
- package/dist/systems/construction-reveal/ghost-dress.js +24 -0
- package/dist/systems/door/door-system.d.ts.map +1 -1
- package/dist/systems/door/door-system.js +55 -17
- package/dist/systems/elevator/elevator-interaction-system.d.ts +7 -0
- package/dist/systems/elevator/elevator-interaction-system.d.ts.map +1 -1
- package/dist/systems/elevator/elevator-interaction-system.js +18 -1
- package/dist/systems/floor-elevation/floor-elevation-system.d.ts +3 -1
- package/dist/systems/floor-elevation/floor-elevation-system.d.ts.map +1 -1
- package/dist/systems/floor-elevation/floor-elevation-system.js +33 -5
- package/dist/systems/geometry/geometry-system.d.ts.map +1 -1
- package/dist/systems/geometry/geometry-system.js +71 -11
- package/dist/systems/interactive/control-widget.d.ts.map +1 -1
- package/dist/systems/interactive/interactive-system.d.ts.map +1 -1
- package/dist/systems/interactive/interactive-system.js +52 -17
- package/dist/systems/interactive/procedural-controls.d.ts +13 -0
- package/dist/systems/interactive/procedural-controls.d.ts.map +1 -0
- package/dist/systems/interactive/procedural-controls.js +17 -0
- package/dist/systems/interactive/scripted-clips.d.ts +19 -0
- package/dist/systems/interactive/scripted-clips.d.ts.map +1 -0
- package/dist/systems/interactive/scripted-clips.js +134 -0
- package/dist/systems/item/item-system.d.ts.map +1 -1
- package/dist/systems/item/item-system.js +9 -3
- package/dist/systems/item/object-cut-invalidation.d.ts +3 -0
- package/dist/systems/item/object-cut-invalidation.d.ts.map +1 -0
- package/dist/systems/item/object-cut-invalidation.js +68 -0
- package/dist/systems/item-light/item-light-system.d.ts +1 -1
- package/dist/systems/item-light/item-light-system.d.ts.map +1 -1
- package/dist/systems/item-light/item-light-system.js +48 -53
- package/dist/systems/level/level-hold.d.ts +4 -0
- package/dist/systems/level/level-hold.d.ts.map +1 -0
- package/dist/systems/level/level-hold.js +14 -0
- package/dist/systems/level/level-system.d.ts.map +1 -1
- package/dist/systems/level/level-system.js +19 -6
- package/dist/systems/level/level-utils.d.ts +19 -6
- package/dist/systems/level/level-utils.d.ts.map +1 -1
- package/dist/systems/level/level-utils.js +68 -9
- package/dist/systems/level/shadow-only-levels.d.ts +9 -0
- package/dist/systems/level/shadow-only-levels.d.ts.map +1 -0
- package/dist/systems/level/shadow-only-levels.js +7 -0
- package/dist/systems/roof/__fixtures__/roof-plane-cases.d.ts +103 -0
- package/dist/systems/roof/__fixtures__/roof-plane-cases.d.ts.map +1 -0
- package/dist/systems/roof/__fixtures__/roof-plane-cases.js +317 -0
- package/dist/systems/roof/roof-materials.d.ts +16 -2
- package/dist/systems/roof/roof-materials.d.ts.map +1 -1
- package/dist/systems/roof/roof-materials.js +64 -31
- package/dist/systems/roof/roof-system.d.ts +21 -4
- package/dist/systems/roof/roof-system.d.ts.map +1 -1
- package/dist/systems/roof/roof-system.js +374 -49
- package/dist/systems/slab/slab-system.d.ts +1 -1
- package/dist/systems/slab/slab-system.d.ts.map +1 -1
- package/dist/systems/slab/slab-system.js +49 -19
- package/dist/systems/stair/stair-system.d.ts.map +1 -1
- package/dist/systems/stair/stair-system.js +133 -354
- package/dist/systems/wall/wall-cutout-cache.d.ts +39 -3
- package/dist/systems/wall/wall-cutout-cache.d.ts.map +1 -1
- package/dist/systems/wall/wall-cutout-cache.js +141 -19
- package/dist/systems/wall/wall-cutout.d.ts +6 -0
- package/dist/systems/wall/wall-cutout.d.ts.map +1 -1
- package/dist/systems/wall/wall-cutout.js +42 -28
- package/dist/systems/wall/wall-finish-data.d.ts +22 -0
- package/dist/systems/wall/wall-finish-data.d.ts.map +1 -0
- package/dist/systems/wall/wall-finish-data.js +17 -0
- package/dist/systems/wall/wall-material-variant.d.ts.map +1 -1
- package/dist/systems/wall/wall-materials.d.ts +9 -4
- package/dist/systems/wall/wall-materials.d.ts.map +1 -1
- package/dist/systems/wall/wall-materials.js +135 -153
- package/dist/systems/wall/wall-placeholder-sweep.d.ts.map +1 -1
- package/dist/systems/wall/wall-system.d.ts +33 -2
- package/dist/systems/wall/wall-system.d.ts.map +1 -1
- package/dist/systems/wall/wall-system.js +621 -159
- package/dist/systems/window/corner-frame.d.ts +10 -0
- package/dist/systems/window/corner-frame.d.ts.map +1 -0
- package/dist/systems/window/corner-frame.js +17 -0
- package/dist/systems/window/window-system.d.ts +1 -1
- package/dist/systems/window/window-system.d.ts.map +1 -1
- package/dist/systems/window/window-system.js +71 -27
- package/package.json +10 -10
- package/dist/systems/fence/fence-system.d.ts +0 -22
- package/dist/systems/fence/fence-system.d.ts.map +0 -1
- package/dist/systems/fence/fence-system.js +0 -357
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { Color, Matrix4, UnsignedByteType } from 'three';
|
|
1
|
+
import { Color, Matrix4, SRGBColorSpace, UnsignedByteType } from 'three';
|
|
2
2
|
import { ssgi } from 'three/addons/tsl/display/SSGINode.js';
|
|
3
3
|
import { denoise } from 'three/examples/jsm/tsl/display/DenoiseNode.js';
|
|
4
4
|
import { fxaa } from 'three/examples/jsm/tsl/display/FXAANode.js';
|
|
5
|
-
import { convertToTexture, diffuseColor, float, mix,
|
|
5
|
+
import { clamp, convertToTexture, diffuseColor, float, mix, normalView, output, renderOutput, sample, saturation, screenUV, smoothstep, uniform, vec2, vec3, vec4, } from 'three/tsl';
|
|
6
6
|
import { PassNode, RenderPipeline, RenderTarget, } from 'three/webgpu';
|
|
7
7
|
import { GRADE_PARAMS, SSGI_PARAMS } from '../components/viewer/post-processing';
|
|
8
|
-
import {
|
|
8
|
+
import { backdropColors, backdropGradient, flatBackdropOutput } from './backdrop';
|
|
9
9
|
import { edgeColorFor, edgeOpacityScaleFor } from './edge-style';
|
|
10
|
-
import { inkedEdges } from './ink-edges';
|
|
11
|
-
import {
|
|
10
|
+
import { createEdgeDepthSampler, inkedEdges } from './ink-edges';
|
|
11
|
+
import { sceneLightingMask, scenePassMrt } from './scene-pass-mrt';
|
|
12
12
|
import { packNormalToRGB, unpackRGBToNormal } from './tsl-compat';
|
|
13
13
|
export const THUMBNAIL_WIDTH = 1920;
|
|
14
14
|
export const THUMBNAIL_HEIGHT = 1080;
|
|
@@ -29,6 +29,11 @@ function clampSnapshotSize(width, height) {
|
|
|
29
29
|
const scale = SNAPSHOT_MAX_EDGE / maxEdge;
|
|
30
30
|
return { w: Math.round(width * scale), h: Math.round(height * scale) };
|
|
31
31
|
}
|
|
32
|
+
function srgbHexToVec3(hex) {
|
|
33
|
+
const color = new Color(hex);
|
|
34
|
+
const { r, g, b } = color.getRGB({ r: 0, g: 0, b: 0 }, SRGBColorSpace);
|
|
35
|
+
return vec3(r, g, b);
|
|
36
|
+
}
|
|
32
37
|
class SnapshotScenePass extends PassNode {
|
|
33
38
|
updateBefore(frame) {
|
|
34
39
|
const renderer = frame.renderer;
|
|
@@ -44,7 +49,7 @@ class SnapshotScenePass extends PassNode {
|
|
|
44
49
|
}
|
|
45
50
|
}
|
|
46
51
|
}
|
|
47
|
-
export async function createSnapshotPipeline({ renderer, scene, camera, atmosphere = null, }) {
|
|
52
|
+
export async function createSnapshotPipeline({ renderer, scene, camera, atmosphere = null, studioBackdrop, }) {
|
|
48
53
|
try {
|
|
49
54
|
if (renderer.init)
|
|
50
55
|
await renderer.init();
|
|
@@ -59,7 +64,9 @@ export async function createSnapshotPipeline({ renderer, scene, camera, atmosphe
|
|
|
59
64
|
const bgProjInvUniform = uniform(new Matrix4());
|
|
60
65
|
const bgCamWorldUniform = uniform(new Matrix4());
|
|
61
66
|
const bgMixUniform = uniform(1);
|
|
67
|
+
const flatBgMixUniform = uniform(0);
|
|
62
68
|
const gradeMixUniform = uniform(0);
|
|
69
|
+
const aoMixUniform = uniform(1);
|
|
63
70
|
const inkMixUniform = uniform(0);
|
|
64
71
|
const inkColorUniform = uniform(new Color('#1a1d24'));
|
|
65
72
|
const inkOpacityUniform = uniform(0.5);
|
|
@@ -67,20 +74,24 @@ export async function createSnapshotPipeline({ renderer, scene, camera, atmosphe
|
|
|
67
74
|
// The scene pass owns MRT and its transparent clear, including when nested
|
|
68
75
|
// inside FXAA's intermediate render target.
|
|
69
76
|
const scenePass = new SnapshotScenePass('color', scene, camera);
|
|
70
|
-
scenePass.setMRT(
|
|
77
|
+
scenePass.setMRT(scenePassMrt({
|
|
71
78
|
output,
|
|
72
79
|
diffuseColor,
|
|
73
80
|
normal: packNormalToRGB(normalView),
|
|
81
|
+
lighting: sceneLightingMask(),
|
|
74
82
|
}));
|
|
75
83
|
const scenePassColor = scenePass.getTextureNode('output');
|
|
76
84
|
const scenePassDepth = scenePass.getTextureNode('depth');
|
|
85
|
+
const sampleEdgeDepth = createEdgeDepthSampler(scenePassDepth, camera);
|
|
77
86
|
const scenePassNormal = scenePass.getTextureNode('normal');
|
|
78
87
|
scenePass.getTexture('diffuseColor').type = UnsignedByteType;
|
|
79
88
|
scenePass.getTexture('normal').type = UnsignedByteType;
|
|
89
|
+
scenePass.getTexture('lighting').type = UnsignedByteType;
|
|
80
90
|
const sceneNormal = sample((uv) => unpackRGBToNormal(scenePassNormal.sample(uv)));
|
|
81
91
|
const giPass = ssgi(scenePassColor, scenePassDepth, sceneNormal, camera);
|
|
82
|
-
|
|
83
|
-
giPass.
|
|
92
|
+
// A saved still has no later frames to hide the viewport's sampling noise.
|
|
93
|
+
giPass.sliceCount.value = 3;
|
|
94
|
+
giPass.stepCount.value = 8;
|
|
84
95
|
giPass.radius.value = SSGI_PARAMS.radius;
|
|
85
96
|
giPass.expFactor.value = SSGI_PARAMS.expFactor;
|
|
86
97
|
giPass.thickness.value = SSGI_PARAMS.thickness;
|
|
@@ -99,18 +110,18 @@ export async function createSnapshotPipeline({ renderer, scene, camera, atmosphe
|
|
|
99
110
|
denoisePass.radius.value = 4;
|
|
100
111
|
// Same far-field AO fade as the viewport pipeline — without it the
|
|
101
112
|
// horizon picks up a visible AO line in captures.
|
|
102
|
-
const aoFarFade = smoothstep(float(0.9994), float(0.9998),
|
|
113
|
+
const aoFarFade = smoothstep(float(0.9994), float(0.9998), sampleEdgeDepth(screenUV));
|
|
103
114
|
const ao = mix(denoisePass.r, float(1), aoFarFade);
|
|
104
|
-
const aoRgb = scenePassColor.rgb.mul(ao);
|
|
115
|
+
const aoRgb = scenePassColor.rgb.mul(mix(float(1), ao, scenePass.getTextureNode('lighting').r.mul(aoMixUniform)));
|
|
105
116
|
// Ink edges, mirroring the viewport pipeline (AO → ink → grade) so
|
|
106
117
|
// captures carry the same soft/strong edge look the canvas shows.
|
|
107
118
|
// Uniform-gated like grade/backdrop: one cached pipeline serves all modes.
|
|
108
119
|
// Radius scales with render height: a supersampled capture (4K → 1080p)
|
|
109
120
|
// would otherwise halve the apparent line weight vs the viewport's 1px.
|
|
110
|
-
const inkRadius =
|
|
121
|
+
const inkRadius = uniform(1);
|
|
111
122
|
const inkedRgb = inkedEdges({
|
|
112
123
|
sceneRgb: aoRgb,
|
|
113
|
-
|
|
124
|
+
sampleDepth: sampleEdgeDepth,
|
|
114
125
|
normalTex: scenePassNormal,
|
|
115
126
|
inkColor: inkColorUniform,
|
|
116
127
|
radius: inkRadius,
|
|
@@ -137,28 +148,63 @@ export async function createSnapshotPipeline({ renderer, scene, camera, atmosphe
|
|
|
137
148
|
const bgGradient = mix(ungradedBgGradient, gradeRgb(ungradedBgGradient), gradeMixUniform);
|
|
138
149
|
const alpha = scenePassColor.a;
|
|
139
150
|
const finalOutput = vec4(mix(sceneRgb, mix(bgGradient, sceneRgb, alpha), bgMixUniform), mix(alpha, float(1), bgMixUniform));
|
|
140
|
-
// FXAA requires a texture node as input; convertToTexture renders finalOutput
|
|
141
|
-
// into an intermediate RT so FXAA can sample it with neighbour UV offsets.
|
|
142
|
-
const aaOutput = fxaa(convertToTexture(finalOutput));
|
|
143
151
|
const pipeline = new RenderPipeline(renderer);
|
|
144
|
-
|
|
152
|
+
if (studioBackdrop) {
|
|
153
|
+
// Composite in display space: the scene gets the renderer's own output
|
|
154
|
+
// transform (tone mapping + sRGB), the backdrop is already sRGB — so
|
|
155
|
+
// the pipeline's final transform is off.
|
|
156
|
+
const { width, height } = renderer.domElement;
|
|
157
|
+
const aspect = width / height;
|
|
158
|
+
const [cx, cy] = studioBackdrop.center;
|
|
159
|
+
const radius = Math.max(...[
|
|
160
|
+
[0, 0],
|
|
161
|
+
[1, 0],
|
|
162
|
+
[0, 1],
|
|
163
|
+
[1, 1],
|
|
164
|
+
].map(([x, y]) => Math.hypot((x - cx) * aspect, y - cy)));
|
|
165
|
+
const offset = screenUV.sub(vec2(cx, cy)).mul(vec2(aspect, 1));
|
|
166
|
+
const t = smoothstep(float(0), float(1), clamp(offset.length().div(radius), 0, 1));
|
|
167
|
+
const backdrop = mix(srgbHexToVec3(studioBackdrop.inner), srgbHexToVec3(studioBackdrop.outer), t);
|
|
168
|
+
const sceneDisplay = renderOutput(vec4(sceneRgb, 1), renderer.toneMapping, renderer.outputColorSpace).rgb;
|
|
169
|
+
pipeline.outputColorTransform = false;
|
|
170
|
+
pipeline.outputNode = fxaa(convertToTexture(vec4(mix(backdrop, sceneDisplay, alpha), 1)));
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
// FXAA requires a texture node as input; convertToTexture renders finalOutput
|
|
174
|
+
// into an intermediate RT so FXAA can sample it with neighbour UV offsets.
|
|
175
|
+
const flatOutput = flatBackdropOutput({
|
|
176
|
+
background: bgColorUniform,
|
|
177
|
+
foreground: sceneRgb,
|
|
178
|
+
alpha,
|
|
179
|
+
toneMapping: renderer.toneMapping,
|
|
180
|
+
outputColorSpace: renderer.outputColorSpace,
|
|
181
|
+
});
|
|
182
|
+
const antialiased = fxaa(convertToTexture(mix(finalOutput, flatOutput, flatBgMixUniform)));
|
|
183
|
+
// One FXAA pass serves both inputs; transform only the linear input at output.
|
|
184
|
+
pipeline.outputColorTransform = false;
|
|
185
|
+
pipeline.outputNode = mix(renderOutput(antialiased, renderer.toneMapping, renderer.outputColorSpace), antialiased, flatBgMixUniform);
|
|
186
|
+
}
|
|
145
187
|
// Dedicated render target — pipeline outputs here instead of the canvas,
|
|
146
188
|
// so R3F's main render loop can never overwrite our capture.
|
|
147
189
|
const { width, height } = renderer.domElement;
|
|
148
190
|
const renderTarget = new RenderTarget(width, height, { depthBuffer: true });
|
|
149
191
|
return {
|
|
150
192
|
applyEnvironment: ({ theme, transparent, grade, edges, camera: captureCamera }) => {
|
|
151
|
-
const sceneTheme =
|
|
193
|
+
const sceneTheme = theme;
|
|
194
|
+
const backdrop = backdropColors(sceneTheme);
|
|
152
195
|
inkMixUniform.value = edges === 'off' ? 0 : 1;
|
|
153
196
|
inkOpacityUniform.value = edges === 'strong' ? 1 : 0.5;
|
|
154
197
|
inkColorUniform.value.set(edgeColorFor(sceneTheme.background));
|
|
155
198
|
inkOpacityScaleUniform.value = edgeOpacityScaleFor(sceneTheme.background);
|
|
156
199
|
bgColorUniform.value.set(sceneTheme.background);
|
|
157
|
-
bgSkyUniform.value.set(
|
|
158
|
-
bgSkyDeepUniform.value.set(
|
|
159
|
-
bgHazeUniform.value.set(
|
|
200
|
+
bgSkyUniform.value.set(backdrop.sky);
|
|
201
|
+
bgSkyDeepUniform.value.set(backdrop.skyDeep);
|
|
202
|
+
bgHazeUniform.value.set(backdrop.haze);
|
|
160
203
|
bgMixUniform.value = transparent ? 0 : 1;
|
|
161
|
-
|
|
204
|
+
flatBgMixUniform.value =
|
|
205
|
+
!transparent && !atmosphere && sceneTheme.backgroundSky === null ? 1 : 0;
|
|
206
|
+
gradeMixUniform.value = grade && sceneTheme.postprocessing?.colorGrading !== false ? 1 : 0;
|
|
207
|
+
aoMixUniform.value = sceneTheme.postprocessing?.ambientOcclusion === false ? 0 : 1;
|
|
162
208
|
// The capture camera never joins the scene graph, so its matrixWorld
|
|
163
209
|
// is only refreshed by the render itself — too late for the backdrop
|
|
164
210
|
// uniforms below.
|
|
@@ -166,10 +212,11 @@ export async function createSnapshotPipeline({ renderer, scene, camera, atmosphe
|
|
|
166
212
|
bgProjInvUniform.value.copy(captureCamera.projectionMatrixInverse);
|
|
167
213
|
bgCamWorldUniform.value.copy(captureCamera.matrixWorld);
|
|
168
214
|
},
|
|
169
|
-
capture: async ({ captureMode, cropRegion, standardSize }) => {
|
|
215
|
+
capture: async ({ captureMode, cropRegion, standardSize, mime }) => {
|
|
170
216
|
const standardW = standardSize?.w ?? THUMBNAIL_WIDTH;
|
|
171
217
|
const standardH = standardSize?.h ?? THUMBNAIL_HEIGHT;
|
|
172
218
|
const { width: captureWidth, height: captureHeight } = renderer.domElement;
|
|
219
|
+
inkRadius.value = Math.max(1, Math.round(captureHeight / 1080));
|
|
173
220
|
// Resize RT if the canvas dimensions changed
|
|
174
221
|
if (renderTarget.width !== captureWidth || renderTarget.height !== captureHeight) {
|
|
175
222
|
renderTarget.setSize(captureWidth, captureHeight);
|
|
@@ -184,100 +231,13 @@ export async function createSnapshotPipeline({ renderer, scene, camera, atmosphe
|
|
|
184
231
|
// Let callers restore visibility and other capture policy immediately
|
|
185
232
|
// after the render, before the asynchronous GPU readback begins.
|
|
186
233
|
await Promise.resolve();
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
// Two readback shapes to handle:
|
|
195
|
-
// - WebGPU (`copyTextureToBuffer`): top-down + 256-byte row padding
|
|
196
|
-
// when width*4 isn't already a multiple of 256.
|
|
197
|
-
// - WebGL2 fallback (iOS Chrome, etc.): tightly-packed but bottom-up
|
|
198
|
-
// (OpenGL framebuffer convention).
|
|
199
|
-
// `isWebGPURenderer` lies — it stays true even when the renderer
|
|
200
|
-
// falls back to the WebGL backend. Inspect the actual backend
|
|
201
|
-
// instead (presence of a GPU device, or backend constructor name).
|
|
202
|
-
const backend = renderer.backend;
|
|
203
|
-
const isWebGPU = !!backend?.device ||
|
|
204
|
-
backend?.isWebGPUBackend === true ||
|
|
205
|
-
backend?.constructor?.name === 'WebGPUBackend';
|
|
206
|
-
let tightPixels;
|
|
207
|
-
if (isWebGPU) {
|
|
208
|
-
// WebGPU: depad rows if needed; orientation is already top-down.
|
|
209
|
-
if (paddedBytesPerRow === actualBytesPerRow) {
|
|
210
|
-
tightPixels = new Uint8ClampedArray(pixels.buffer, pixels.byteOffset, Math.min(pixels.byteLength, tightTotal));
|
|
211
|
-
}
|
|
212
|
-
else {
|
|
213
|
-
tightPixels = new Uint8ClampedArray(tightTotal);
|
|
214
|
-
for (let row = 0; row < captureHeight; row++) {
|
|
215
|
-
tightPixels.set(pixels.subarray(row * paddedBytesPerRow, row * paddedBytesPerRow + actualBytesPerRow), row * actualBytesPerRow);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
else {
|
|
220
|
-
// WebGL2: tight buffer in bottom-up order — flip rows.
|
|
221
|
-
tightPixels = new Uint8ClampedArray(tightTotal);
|
|
222
|
-
for (let row = 0; row < captureHeight; row++) {
|
|
223
|
-
const srcStart = (captureHeight - 1 - row) * actualBytesPerRow;
|
|
224
|
-
tightPixels.set(pixels.subarray(srcStart, srcStart + actualBytesPerRow), row * actualBytesPerRow);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
const imageData = new ImageData(tightPixels, captureWidth, captureHeight);
|
|
228
|
-
const srcCanvas = new OffscreenCanvas(captureWidth, captureHeight);
|
|
229
|
-
srcCanvas.getContext('2d').putImageData(imageData, 0, 0);
|
|
230
|
-
let outW;
|
|
231
|
-
let outH;
|
|
232
|
-
let blob;
|
|
233
|
-
if (captureMode === 'viewport') {
|
|
234
|
-
;
|
|
235
|
-
({ w: outW, h: outH } = clampSnapshotSize(captureWidth, captureHeight));
|
|
236
|
-
const offscreen = new OffscreenCanvas(outW, outH);
|
|
237
|
-
const ctx = offscreen.getContext('2d');
|
|
238
|
-
if (outW !== captureWidth || outH !== captureHeight)
|
|
239
|
-
ctx.imageSmoothingQuality = 'high';
|
|
240
|
-
ctx.drawImage(srcCanvas, 0, 0, captureWidth, captureHeight, 0, 0, outW, outH);
|
|
241
|
-
blob = await offscreen.convertToBlob({ type: SNAPSHOT_MIME, quality: SNAPSHOT_QUALITY });
|
|
242
|
-
}
|
|
243
|
-
else if (captureMode === 'area' && cropRegion) {
|
|
244
|
-
const sx = Math.round(cropRegion.x * captureWidth);
|
|
245
|
-
const sy = Math.round(cropRegion.y * captureHeight);
|
|
246
|
-
const sourceW = Math.round(cropRegion.width * captureWidth);
|
|
247
|
-
const sourceH = Math.round(cropRegion.height * captureHeight);
|
|
248
|
-
({ w: outW, h: outH } = clampSnapshotSize(sourceW, sourceH));
|
|
249
|
-
const offscreen = new OffscreenCanvas(outW, outH);
|
|
250
|
-
const ctx = offscreen.getContext('2d');
|
|
251
|
-
if (outW !== sourceW || outH !== sourceH)
|
|
252
|
-
ctx.imageSmoothingQuality = 'high';
|
|
253
|
-
ctx.drawImage(srcCanvas, sx, sy, sourceW, sourceH, 0, 0, outW, outH);
|
|
254
|
-
blob = await offscreen.convertToBlob({ type: SNAPSHOT_MIME, quality: SNAPSHOT_QUALITY });
|
|
255
|
-
}
|
|
256
|
-
else {
|
|
257
|
-
// Standard: center-crop to the requested aspect (default 1920×1080)
|
|
258
|
-
const srcAspect = captureWidth / captureHeight;
|
|
259
|
-
const dstAspect = standardW / standardH;
|
|
260
|
-
let sx = 0;
|
|
261
|
-
let sy = 0;
|
|
262
|
-
let sWidth = captureWidth;
|
|
263
|
-
let sHeight = captureHeight;
|
|
264
|
-
if (srcAspect > dstAspect) {
|
|
265
|
-
sWidth = Math.round(captureHeight * dstAspect);
|
|
266
|
-
sx = Math.round((captureWidth - sWidth) / 2);
|
|
267
|
-
}
|
|
268
|
-
else if (srcAspect < dstAspect) {
|
|
269
|
-
sHeight = Math.round(captureWidth / dstAspect);
|
|
270
|
-
sy = Math.round((captureHeight - sHeight) / 2);
|
|
271
|
-
}
|
|
272
|
-
outW = standardW;
|
|
273
|
-
outH = standardH;
|
|
274
|
-
const offscreen = new OffscreenCanvas(outW, outH);
|
|
275
|
-
offscreen
|
|
276
|
-
.getContext('2d')
|
|
277
|
-
.drawImage(srcCanvas, sx, sy, sWidth, sHeight, 0, 0, outW, outH);
|
|
278
|
-
blob = await offscreen.convertToBlob({ type: SNAPSHOT_MIME, quality: SNAPSHOT_QUALITY });
|
|
279
|
-
}
|
|
280
|
-
return { blob, outW, outH };
|
|
234
|
+
return encodeCapture(renderer, renderTarget, captureWidth, captureHeight, {
|
|
235
|
+
captureMode,
|
|
236
|
+
cropRegion,
|
|
237
|
+
standardW,
|
|
238
|
+
standardH,
|
|
239
|
+
mime,
|
|
240
|
+
});
|
|
281
241
|
},
|
|
282
242
|
dispose: () => {
|
|
283
243
|
pipeline.dispose();
|
|
@@ -292,3 +252,173 @@ export async function createSnapshotPipeline({ renderer, scene, camera, atmosphe
|
|
|
292
252
|
return null;
|
|
293
253
|
}
|
|
294
254
|
}
|
|
255
|
+
/**
|
|
256
|
+
* The frame in `renderTarget`, read back and encoded: WebGPU's 256-byte row
|
|
257
|
+
* padding undone (the WebGL fallback's bottom-up rows flipped), then cut to
|
|
258
|
+
* the capture mode — the viewport as is, an area, or the standard aspect.
|
|
259
|
+
* Shared by the post-processed pipeline and the plain one.
|
|
260
|
+
*/
|
|
261
|
+
async function encodeCapture(renderer, renderTarget, captureWidth, captureHeight, { captureMode, cropRegion, standardW, standardH, mime, }) {
|
|
262
|
+
const encoding = mime ? { type: mime } : { type: SNAPSHOT_MIME, quality: SNAPSHOT_QUALITY };
|
|
263
|
+
// Read pixels from the RT asynchronously.
|
|
264
|
+
// WebGPU copyTextureToBuffer aligns each row to 256 bytes, so we must
|
|
265
|
+
// depad the rows before constructing ImageData.
|
|
266
|
+
const pixels = (await renderer.readRenderTargetPixelsAsync(renderTarget, 0, 0, captureWidth, captureHeight));
|
|
267
|
+
const actualBytesPerRow = captureWidth * 4;
|
|
268
|
+
const tightTotal = actualBytesPerRow * captureHeight;
|
|
269
|
+
const paddedBytesPerRow = Math.ceil(actualBytesPerRow / 256) * 256;
|
|
270
|
+
// Two readback shapes to handle:
|
|
271
|
+
// - WebGPU (`copyTextureToBuffer`): top-down + 256-byte row padding
|
|
272
|
+
// when width*4 isn't already a multiple of 256.
|
|
273
|
+
// - WebGL2 fallback (iOS Chrome, etc.): tightly-packed but bottom-up
|
|
274
|
+
// (OpenGL framebuffer convention).
|
|
275
|
+
// `isWebGPURenderer` lies — it stays true even when the renderer
|
|
276
|
+
// falls back to the WebGL backend. Inspect the actual backend
|
|
277
|
+
// instead (presence of a GPU device, or backend constructor name).
|
|
278
|
+
const backend = renderer.backend;
|
|
279
|
+
const isWebGPU = !!backend?.device ||
|
|
280
|
+
backend?.isWebGPUBackend === true ||
|
|
281
|
+
backend?.constructor?.name === 'WebGPUBackend';
|
|
282
|
+
let tightPixels;
|
|
283
|
+
if (isWebGPU) {
|
|
284
|
+
// WebGPU: depad rows if needed; orientation is already top-down.
|
|
285
|
+
if (paddedBytesPerRow === actualBytesPerRow) {
|
|
286
|
+
tightPixels = new Uint8ClampedArray(pixels.buffer, pixels.byteOffset, Math.min(pixels.byteLength, tightTotal));
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
tightPixels = new Uint8ClampedArray(tightTotal);
|
|
290
|
+
for (let row = 0; row < captureHeight; row++) {
|
|
291
|
+
tightPixels.set(pixels.subarray(row * paddedBytesPerRow, row * paddedBytesPerRow + actualBytesPerRow), row * actualBytesPerRow);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
// WebGL2: tight buffer in bottom-up order — flip rows.
|
|
297
|
+
tightPixels = new Uint8ClampedArray(tightTotal);
|
|
298
|
+
for (let row = 0; row < captureHeight; row++) {
|
|
299
|
+
const srcStart = (captureHeight - 1 - row) * actualBytesPerRow;
|
|
300
|
+
tightPixels.set(pixels.subarray(srcStart, srcStart + actualBytesPerRow), row * actualBytesPerRow);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
const imageData = new ImageData(tightPixels, captureWidth, captureHeight);
|
|
304
|
+
const srcCanvas = new OffscreenCanvas(captureWidth, captureHeight);
|
|
305
|
+
srcCanvas.getContext('2d').putImageData(imageData, 0, 0);
|
|
306
|
+
let outW;
|
|
307
|
+
let outH;
|
|
308
|
+
let blob;
|
|
309
|
+
if (captureMode === 'viewport') {
|
|
310
|
+
;
|
|
311
|
+
({ w: outW, h: outH } = clampSnapshotSize(captureWidth, captureHeight));
|
|
312
|
+
const offscreen = new OffscreenCanvas(outW, outH);
|
|
313
|
+
const ctx = offscreen.getContext('2d');
|
|
314
|
+
if (outW !== captureWidth || outH !== captureHeight)
|
|
315
|
+
ctx.imageSmoothingQuality = 'high';
|
|
316
|
+
ctx.drawImage(srcCanvas, 0, 0, captureWidth, captureHeight, 0, 0, outW, outH);
|
|
317
|
+
blob = await offscreen.convertToBlob(encoding);
|
|
318
|
+
}
|
|
319
|
+
else if (captureMode === 'area' && cropRegion) {
|
|
320
|
+
const sx = Math.round(cropRegion.x * captureWidth);
|
|
321
|
+
const sy = Math.round(cropRegion.y * captureHeight);
|
|
322
|
+
const sourceW = Math.round(cropRegion.width * captureWidth);
|
|
323
|
+
const sourceH = Math.round(cropRegion.height * captureHeight);
|
|
324
|
+
({ w: outW, h: outH } = clampSnapshotSize(sourceW, sourceH));
|
|
325
|
+
const offscreen = new OffscreenCanvas(outW, outH);
|
|
326
|
+
const ctx = offscreen.getContext('2d');
|
|
327
|
+
if (outW !== sourceW || outH !== sourceH)
|
|
328
|
+
ctx.imageSmoothingQuality = 'high';
|
|
329
|
+
ctx.drawImage(srcCanvas, sx, sy, sourceW, sourceH, 0, 0, outW, outH);
|
|
330
|
+
blob = await offscreen.convertToBlob(encoding);
|
|
331
|
+
}
|
|
332
|
+
else {
|
|
333
|
+
// Standard: center-crop to the requested aspect (default 1920×1080)
|
|
334
|
+
const srcAspect = captureWidth / captureHeight;
|
|
335
|
+
const dstAspect = standardW / standardH;
|
|
336
|
+
let sx = 0;
|
|
337
|
+
let sy = 0;
|
|
338
|
+
let sWidth = captureWidth;
|
|
339
|
+
let sHeight = captureHeight;
|
|
340
|
+
if (srcAspect > dstAspect) {
|
|
341
|
+
sWidth = Math.round(captureHeight * dstAspect);
|
|
342
|
+
sx = Math.round((captureWidth - sWidth) / 2);
|
|
343
|
+
}
|
|
344
|
+
else if (srcAspect < dstAspect) {
|
|
345
|
+
sHeight = Math.round(captureWidth / dstAspect);
|
|
346
|
+
sy = Math.round((captureHeight - sHeight) / 2);
|
|
347
|
+
}
|
|
348
|
+
outW = standardW;
|
|
349
|
+
outH = standardH;
|
|
350
|
+
const offscreen = new OffscreenCanvas(outW, outH);
|
|
351
|
+
offscreen.getContext('2d').drawImage(srcCanvas, sx, sy, sWidth, sHeight, 0, 0, outW, outH);
|
|
352
|
+
blob = await offscreen.convertToBlob(encoding);
|
|
353
|
+
}
|
|
354
|
+
return { blob, outW, outH };
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* A capture WITHOUT the post-processing stack: the scene as the renderer
|
|
358
|
+
* draws it, into an offscreen target, read back and encoded like the
|
|
359
|
+
* post-processed frames. The sheets' orthographic elevation captures use
|
|
360
|
+
* it (2026-09-10): an SSGI pass built for a second, orthographic camera
|
|
361
|
+
* never delivered a frame and held the generator's guard forever.
|
|
362
|
+
*/
|
|
363
|
+
export async function createPlainSnapshotPipeline({ renderer, scene, camera, }) {
|
|
364
|
+
try {
|
|
365
|
+
if (renderer.init)
|
|
366
|
+
await renderer.init();
|
|
367
|
+
const { width, height } = renderer.domElement;
|
|
368
|
+
const renderTarget = new RenderTarget(width, height, { depthBuffer: true });
|
|
369
|
+
let transparent = true;
|
|
370
|
+
const background = new Color('#ffffff');
|
|
371
|
+
return {
|
|
372
|
+
applyEnvironment: ({ theme, transparent: alpha }) => {
|
|
373
|
+
transparent = alpha;
|
|
374
|
+
background.set(theme.background);
|
|
375
|
+
},
|
|
376
|
+
capture: async ({ captureMode, cropRegion, standardSize }) => {
|
|
377
|
+
const standardW = standardSize?.w ?? THUMBNAIL_WIDTH;
|
|
378
|
+
const standardH = standardSize?.h ?? THUMBNAIL_HEIGHT;
|
|
379
|
+
const { width: captureWidth, height: captureHeight } = renderer.domElement;
|
|
380
|
+
if (renderTarget.width !== captureWidth || renderTarget.height !== captureHeight) {
|
|
381
|
+
renderTarget.setSize(captureWidth, captureHeight);
|
|
382
|
+
}
|
|
383
|
+
const previousBackground = scene.background;
|
|
384
|
+
const previousClear = renderer.getClearColor(new Color());
|
|
385
|
+
const previousAlpha = renderer.getClearAlpha();
|
|
386
|
+
try {
|
|
387
|
+
scene.background = null;
|
|
388
|
+
renderer.setClearColor(background, transparent ? 0 : 1);
|
|
389
|
+
renderer.setRenderTarget(renderTarget);
|
|
390
|
+
// outside the frame loop the async render is the one that submits
|
|
391
|
+
// its work and settles once the GPU took it
|
|
392
|
+
const asyncRender = renderer.renderAsync;
|
|
393
|
+
if (asyncRender)
|
|
394
|
+
await asyncRender.call(renderer, scene, camera);
|
|
395
|
+
else
|
|
396
|
+
renderer.render(scene, camera);
|
|
397
|
+
}
|
|
398
|
+
finally {
|
|
399
|
+
renderer.setRenderTarget(null);
|
|
400
|
+
scene.background = previousBackground;
|
|
401
|
+
renderer.setClearColor(previousClear, previousAlpha);
|
|
402
|
+
}
|
|
403
|
+
await Promise.resolve();
|
|
404
|
+
// a readback that never returns must not hold a caller for the session
|
|
405
|
+
return Promise.race([
|
|
406
|
+
encodeCapture(renderer, renderTarget, captureWidth, captureHeight, {
|
|
407
|
+
captureMode,
|
|
408
|
+
cropRegion,
|
|
409
|
+
standardW,
|
|
410
|
+
standardH,
|
|
411
|
+
}),
|
|
412
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('the plain capture readback did not return within 12 s')), 12000)),
|
|
413
|
+
]);
|
|
414
|
+
},
|
|
415
|
+
dispose: () => {
|
|
416
|
+
renderTarget.dispose();
|
|
417
|
+
},
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
catch (error) {
|
|
421
|
+
console.error('[thumbnail] Failed to build the plain capture pipeline.', error);
|
|
422
|
+
return null;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"terrain-perimeter-fill.d.ts","sourceRoot":"","sources":["../../src/lib/terrain-perimeter-fill.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAA+C,MAAM,OAAO,CAAA;AAGnF,MAAM,MAAM,qBAAqB,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAE5D,wBAAgB,iCAAiC,CAC/C,MAAM,EAAE,SAAS,qBAAqB,EAAE,EACxC,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,IAAI,EAAE,MAAM,EACZ,OAAO,SAAO,GACb,cAAc,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"terrain-perimeter-fill.d.ts","sourceRoot":"","sources":["../../src/lib/terrain-perimeter-fill.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAA+C,MAAM,OAAO,CAAA;AAGnF,MAAM,MAAM,qBAAqB,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAE5D,wBAAgB,iCAAiC,CAC/C,MAAM,EAAE,SAAS,qBAAqB,EAAE,EACxC,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,IAAI,EAAE,MAAM,EACZ,OAAO,SAAO,GACb,cAAc,GAAG,IAAI,CAkFvB"}
|
|
@@ -13,32 +13,39 @@ export function buildTerrainPerimeterFillGeometry(points, bottomY, topY, epsilon
|
|
|
13
13
|
}
|
|
14
14
|
const counterClockwise = signedArea > 0;
|
|
15
15
|
const positions = [];
|
|
16
|
-
const
|
|
16
|
+
const uvs = [];
|
|
17
|
+
const push = (point, y, u = point.x, v = point.z) => {
|
|
17
18
|
positions.push(point.x, y, point.z);
|
|
19
|
+
uvs.push(u, v);
|
|
18
20
|
};
|
|
21
|
+
let perimeter = 0;
|
|
19
22
|
for (let index = 0; index < points.length; index += 1) {
|
|
20
23
|
const next = (index + 1) % points.length;
|
|
21
24
|
const a = points[index];
|
|
22
25
|
const b = points[next];
|
|
23
26
|
const ay = bottomY[index];
|
|
24
27
|
const by = bottomY[next];
|
|
25
|
-
|
|
28
|
+
const length = Math.hypot(b.x - a.x, b.z - a.z);
|
|
29
|
+
const ua = perimeter;
|
|
30
|
+
perimeter += length;
|
|
31
|
+
const ub = perimeter;
|
|
32
|
+
if (length < epsilon || (ay >= topY - epsilon && by >= topY - epsilon))
|
|
26
33
|
continue;
|
|
27
34
|
if (counterClockwise) {
|
|
28
|
-
push(a, topY);
|
|
29
|
-
push(b, by);
|
|
30
|
-
push(a, ay);
|
|
31
|
-
push(a, topY);
|
|
32
|
-
push(b, topY);
|
|
33
|
-
push(b, by);
|
|
35
|
+
push(a, topY, ua, topY);
|
|
36
|
+
push(b, by, ub, by);
|
|
37
|
+
push(a, ay, ua, ay);
|
|
38
|
+
push(a, topY, ua, topY);
|
|
39
|
+
push(b, topY, ub, topY);
|
|
40
|
+
push(b, by, ub, by);
|
|
34
41
|
}
|
|
35
42
|
else {
|
|
36
|
-
push(a, topY);
|
|
37
|
-
push(a, ay);
|
|
38
|
-
push(b, by);
|
|
39
|
-
push(a, topY);
|
|
40
|
-
push(b, by);
|
|
41
|
-
push(b, topY);
|
|
43
|
+
push(a, topY, ua, topY);
|
|
44
|
+
push(a, ay, ua, ay);
|
|
45
|
+
push(b, by, ub, by);
|
|
46
|
+
push(a, topY, ua, topY);
|
|
47
|
+
push(b, by, ub, by);
|
|
48
|
+
push(b, topY, ub, topY);
|
|
42
49
|
}
|
|
43
50
|
}
|
|
44
51
|
const faces = ShapeUtils.triangulateShape(points.map((point) => new Vector2(point.x, point.z)), []);
|
|
@@ -69,6 +76,7 @@ export function buildTerrainPerimeterFillGeometry(points, bottomY, topY, epsilon
|
|
|
69
76
|
return null;
|
|
70
77
|
const geometry = new BufferGeometry();
|
|
71
78
|
geometry.setAttribute('position', new Float32BufferAttribute(positions, 3));
|
|
79
|
+
geometry.setAttribute('uv', new Float32BufferAttribute(uvs, 2));
|
|
72
80
|
geometry.computeVertexNormals();
|
|
73
81
|
ensureRenderableGeometryAttributes(geometry);
|
|
74
82
|
return geometry;
|
package/dist/lib/tsl-compat.d.ts
CHANGED
|
@@ -7,6 +7,6 @@
|
|
|
7
7
|
* @types/three pin) once tsgo handles the 0.185 types; the `typeof` references
|
|
8
8
|
* to the removed old names will fail the build as a reminder.
|
|
9
9
|
*/
|
|
10
|
-
export declare const packNormalToRGB: (node: import("three/webgpu").Node) => import("three/webgpu").Node<"vec3">;
|
|
11
|
-
export declare const unpackRGBToNormal: (node: import("three/webgpu").Node) => import("three/webgpu").Node<"vec3">;
|
|
10
|
+
export declare const packNormalToRGB: (node: import("three/webgpu").Node<"vec3"> | import("three").Vector3) => import("three/webgpu").Node<"vec3">;
|
|
11
|
+
export declare const unpackRGBToNormal: (node: import("three/webgpu").Node<"vec3"> | import("three/webgpu").Node<"vec4"> | import("three").Vector3) => import("three/webgpu").Node<"vec3">;
|
|
12
12
|
//# sourceMappingURL=tsl-compat.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tsl-compat.d.ts","sourceRoot":"","sources":["../../src/lib/tsl-compat.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,
|
|
1
|
+
{"version":3,"file":"tsl-compat.d.ts","sourceRoot":"","sources":["../../src/lib/tsl-compat.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,8GACV,CAAA;AAClB,eAAO,MAAM,iBAAiB,oJAEX,CAAA"}
|