@pascal-app/viewer 1.0.6 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -21
- package/dist/components/viewer/daylight-atmosphere.d.ts +9 -0
- package/dist/components/viewer/daylight-atmosphere.d.ts.map +1 -0
- package/dist/components/viewer/daylight-atmosphere.js +315 -0
- package/dist/components/viewer/drone-controller.d.ts +15 -0
- package/dist/components/viewer/drone-controller.d.ts.map +1 -0
- package/dist/components/viewer/drone-controller.js +194 -0
- package/dist/components/viewer/glb-interactive.d.ts +4 -18
- package/dist/components/viewer/glb-interactive.d.ts.map +1 -1
- package/dist/components/viewer/glb-interactive.js +32 -268
- package/dist/components/viewer/glb-replace-instances.d.ts +6 -3
- package/dist/components/viewer/glb-replace-instances.d.ts.map +1 -1
- package/dist/components/viewer/glb-replace-instances.js +5 -4
- package/dist/components/viewer/glb-scene-motion.d.ts +8 -0
- package/dist/components/viewer/glb-scene-motion.d.ts.map +1 -0
- package/dist/components/viewer/glb-scene-motion.js +57 -0
- package/dist/components/viewer/glb-scene.d.ts +5 -1
- package/dist/components/viewer/glb-scene.d.ts.map +1 -1
- package/dist/components/viewer/glb-scene.js +290 -49
- package/dist/components/viewer/glb-selection-callout.d.ts +17 -0
- package/dist/components/viewer/glb-selection-callout.d.ts.map +1 -0
- package/dist/components/viewer/glb-selection-callout.js +54 -0
- package/dist/components/viewer/glb-walkthrough-controller.d.ts.map +1 -1
- package/dist/components/viewer/glb-walkthrough-controller.js +6 -3
- package/dist/components/viewer/index.d.ts +5 -0
- package/dist/components/viewer/index.d.ts.map +1 -1
- package/dist/components/viewer/index.js +38 -10
- package/dist/components/viewer/lights.d.ts.map +1 -1
- package/dist/components/viewer/lights.js +192 -5
- package/dist/components/viewer/post-processing.d.ts.map +1 -1
- package/dist/components/viewer/post-processing.js +150 -94
- package/dist/components/viewer/registered-systems.d.ts.map +1 -1
- package/dist/components/viewer/registered-systems.js +2 -2
- package/dist/components/viewer/scene-environment.js +4 -5
- package/dist/components/viewer/selection-callout.d.ts +51 -0
- package/dist/components/viewer/selection-callout.d.ts.map +1 -0
- package/dist/components/viewer/selection-callout.js +197 -0
- package/dist/components/viewer/unsupported-gpu-fallback.d.ts.map +1 -1
- package/dist/components/viewer/unsupported-gpu-fallback.js +21 -1
- package/dist/components/viewer/use-daylight-rooms.d.ts +13 -0
- package/dist/components/viewer/use-daylight-rooms.d.ts.map +1 -0
- package/dist/components/viewer/use-daylight-rooms.js +278 -0
- package/dist/components/viewer/viewer-camera.d.ts.map +1 -1
- package/dist/components/viewer/viewer-camera.js +12 -1
- package/dist/hooks/use-gltf-ktx2.d.ts.map +1 -1
- package/dist/hooks/use-gltf-ktx2.js +2 -1
- package/dist/index.d.ts +26 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +28 -8
- package/dist/lib/asset-url.d.ts +0 -1
- package/dist/lib/asset-url.d.ts.map +1 -1
- package/dist/lib/asset-url.js +3 -3
- package/dist/lib/daylight/ground.d.ts +21 -0
- package/dist/lib/daylight/ground.d.ts.map +1 -0
- package/dist/lib/daylight/ground.js +53 -0
- package/dist/lib/daylight/location.d.ts +32 -0
- package/dist/lib/daylight/location.d.ts.map +1 -0
- package/dist/lib/daylight/location.js +74 -0
- package/dist/lib/daylight/look.d.ts +33 -0
- package/dist/lib/daylight/look.d.ts.map +1 -0
- package/dist/lib/daylight/look.js +13 -0
- package/dist/lib/daylight/material-hooks.d.ts +2 -0
- package/dist/lib/daylight/material-hooks.d.ts.map +1 -0
- package/dist/lib/daylight/material-hooks.js +150 -0
- package/dist/lib/daylight/room-context.d.ts +12 -0
- package/dist/lib/daylight/room-context.d.ts.map +1 -0
- package/dist/lib/daylight/room-context.js +54 -0
- package/dist/lib/daylight/room-map.d.ts +124 -0
- package/dist/lib/daylight/room-map.d.ts.map +1 -0
- package/dist/lib/daylight/room-map.js +630 -0
- package/dist/lib/daylight/room-scene.d.ts +56 -0
- package/dist/lib/daylight/room-scene.d.ts.map +1 -0
- package/dist/lib/daylight/room-scene.js +391 -0
- package/dist/lib/daylight/rooms.d.ts +292 -0
- package/dist/lib/daylight/rooms.d.ts.map +1 -0
- package/dist/lib/daylight/rooms.js +1268 -0
- package/dist/lib/daylight/runtime.d.ts +33 -0
- package/dist/lib/daylight/runtime.d.ts.map +1 -0
- package/dist/lib/daylight/runtime.js +42 -0
- package/dist/lib/daylight/sky.d.ts +26 -0
- package/dist/lib/daylight/sky.d.ts.map +1 -0
- package/dist/lib/daylight/sky.js +217 -0
- package/dist/lib/daylight/solar.d.ts +46 -0
- package/dist/lib/daylight/solar.d.ts.map +1 -0
- package/dist/lib/daylight/solar.js +156 -0
- package/dist/lib/device-tier.d.ts +29 -0
- package/dist/lib/device-tier.d.ts.map +1 -0
- package/dist/lib/device-tier.js +71 -0
- package/dist/lib/dispose-object3d.d.ts.map +1 -1
- package/dist/lib/dispose-object3d.js +4 -2
- package/dist/lib/drone-flight.d.ts +44 -0
- package/dist/lib/drone-flight.d.ts.map +1 -0
- package/dist/lib/drone-flight.js +121 -0
- package/dist/lib/editor-highlight.d.ts +16 -0
- package/dist/lib/editor-highlight.d.ts.map +1 -0
- package/dist/lib/editor-highlight.js +33 -0
- package/dist/lib/foundation-materials.d.ts.map +1 -1
- package/dist/lib/foundation-materials.js +2 -4
- package/dist/lib/glb-merge.d.ts +23 -0
- package/dist/lib/glb-merge.d.ts.map +1 -0
- package/dist/lib/glb-merge.js +184 -0
- package/dist/lib/hardware-geometry.d.ts +21 -0
- package/dist/lib/hardware-geometry.d.ts.map +1 -0
- package/dist/lib/hardware-geometry.js +283 -0
- package/dist/lib/ktx2-loader.d.ts.map +1 -1
- package/dist/lib/ktx2-loader.js +2 -1
- package/dist/lib/layer-pass.d.ts +2 -0
- package/dist/lib/layer-pass.d.ts.map +1 -1
- package/dist/lib/layer-pass.js +20 -0
- package/dist/lib/materials.d.ts.map +1 -1
- package/dist/lib/materials.js +50 -23
- package/dist/lib/measurement-label.d.ts +4 -0
- package/dist/lib/measurement-label.d.ts.map +1 -0
- package/dist/lib/measurement-label.js +54 -0
- package/dist/lib/measurement-presentation.d.ts +5 -0
- package/dist/lib/measurement-presentation.d.ts.map +1 -0
- package/dist/lib/measurement-presentation.js +8 -0
- package/dist/lib/merged-outline-node.d.ts.map +1 -1
- package/dist/lib/merged-outline-node.js +7 -1
- package/dist/lib/occlusion-culler.d.ts +34 -0
- package/dist/lib/occlusion-culler.d.ts.map +1 -0
- package/dist/lib/occlusion-culler.js +698 -0
- package/dist/lib/pointer-events.d.ts.map +1 -1
- package/dist/lib/pointer-events.js +3 -3
- package/dist/lib/post-processing-resources.d.ts +4 -0
- package/dist/lib/post-processing-resources.d.ts.map +1 -1
- package/dist/lib/post-processing-resources.js +5 -0
- package/dist/lib/readback-rows.d.ts +14 -0
- package/dist/lib/readback-rows.d.ts.map +1 -0
- package/dist/lib/readback-rows.js +31 -0
- package/dist/lib/reveal-pose.d.ts.map +1 -1
- package/dist/lib/reveal-pose.js +3 -0
- package/dist/lib/scene-matrices.d.ts +12 -0
- package/dist/lib/scene-matrices.d.ts.map +1 -0
- package/dist/lib/scene-matrices.js +287 -0
- package/dist/lib/scene-pass-mrt.d.ts +5 -2
- package/dist/lib/scene-pass-mrt.d.ts.map +1 -1
- package/dist/lib/scene-pass-mrt.js +7 -2
- package/dist/lib/scene-themes.d.ts +0 -2
- package/dist/lib/scene-themes.d.ts.map +1 -1
- package/dist/lib/scene-themes.js +0 -2
- package/dist/lib/scene-visibility.d.ts +2 -0
- package/dist/lib/scene-visibility.d.ts.map +1 -1
- package/dist/lib/scene-visibility.js +12 -3
- package/dist/lib/site-surfaces.d.ts +1 -8
- package/dist/lib/site-surfaces.d.ts.map +1 -1
- package/dist/lib/site-surfaces.js +0 -19
- package/dist/lib/snapshot-pipeline.d.ts.map +1 -1
- package/dist/lib/snapshot-pipeline.js +24 -40
- package/dist/lib/texture-reference.d.ts.map +1 -1
- package/dist/lib/texture-reference.js +13 -10
- package/dist/lib/underground-ambiance.d.ts +0 -2
- package/dist/lib/underground-ambiance.d.ts.map +1 -1
- package/dist/lib/underground-ambiance.js +0 -2
- package/dist/lib/viewer-config.d.ts +21 -0
- package/dist/lib/viewer-config.d.ts.map +1 -0
- package/dist/lib/viewer-config.js +21 -0
- package/dist/lib/viewer-preference-storage.d.ts +7 -0
- package/dist/lib/viewer-preference-storage.d.ts.map +1 -0
- package/dist/lib/viewer-preference-storage.js +23 -0
- package/dist/store/use-item-light-pool.d.ts +3 -6
- package/dist/store/use-item-light-pool.d.ts.map +1 -1
- package/dist/store/use-item-light-pool.js +12 -0
- package/dist/store/use-viewer.d.ts +62 -59
- package/dist/store/use-viewer.d.ts.map +1 -1
- package/dist/store/use-viewer.js +106 -19
- package/dist/systems/ceiling/ceiling-system.d.ts +5 -0
- package/dist/systems/ceiling/ceiling-system.d.ts.map +1 -1
- package/dist/systems/ceiling/ceiling-system.js +11 -1
- package/dist/systems/door/door-system.d.ts.map +1 -1
- package/dist/systems/door/door-system.js +501 -145
- package/dist/systems/door/leaf-profile.d.ts +11 -0
- package/dist/systems/door/leaf-profile.d.ts.map +1 -0
- package/dist/systems/door/leaf-profile.js +140 -0
- package/dist/systems/elevator/elevator-interaction-system.d.ts +16 -2
- package/dist/systems/elevator/elevator-interaction-system.d.ts.map +1 -1
- package/dist/systems/elevator/elevator-interaction-system.js +41 -8
- package/dist/systems/geometry/geometry-status.d.ts +8 -0
- package/dist/systems/geometry/geometry-status.d.ts.map +1 -0
- package/dist/systems/geometry/geometry-status.js +24 -0
- package/dist/systems/interactive/callout-actions.d.ts +72 -0
- package/dist/systems/interactive/callout-actions.d.ts.map +1 -0
- package/dist/systems/interactive/callout-actions.js +212 -0
- package/dist/systems/interactive/interactive-system.d.ts +5 -1
- package/dist/systems/interactive/interactive-system.d.ts.map +1 -1
- package/dist/systems/interactive/interactive-system.js +44 -121
- package/dist/systems/interactive/opening-settled.d.ts +16 -0
- package/dist/systems/interactive/opening-settled.d.ts.map +1 -0
- package/dist/systems/interactive/opening-settled.js +53 -0
- package/dist/systems/item-light/item-light-system.d.ts.map +1 -1
- package/dist/systems/item-light/item-light-system.js +22 -214
- package/dist/systems/item-light/pooled-lights.d.ts +70 -0
- package/dist/systems/item-light/pooled-lights.d.ts.map +1 -0
- package/dist/systems/item-light/pooled-lights.js +449 -0
- package/dist/systems/level/level-system.d.ts.map +1 -1
- package/dist/systems/level/level-system.js +19 -1
- package/dist/systems/roof/roof-material-slots.d.ts +14 -0
- package/dist/systems/roof/roof-material-slots.d.ts.map +1 -0
- package/dist/systems/roof/roof-material-slots.js +13 -0
- package/dist/systems/roof/roof-materials.d.ts +25 -2
- package/dist/systems/roof/roof-materials.d.ts.map +1 -1
- package/dist/systems/roof/roof-materials.js +85 -26
- package/dist/systems/roof/roof-system.d.ts +34 -1
- package/dist/systems/roof/roof-system.d.ts.map +1 -1
- package/dist/systems/roof/roof-system.js +582 -168
- package/dist/systems/wall/opening-casing-geometry.d.ts +14 -0
- package/dist/systems/wall/opening-casing-geometry.d.ts.map +1 -0
- package/dist/systems/wall/opening-casing-geometry.js +102 -0
- package/dist/systems/wall/opening-cutout-geometry.d.ts +9 -0
- package/dist/systems/wall/opening-cutout-geometry.d.ts.map +1 -1
- package/dist/systems/wall/opening-cutout-geometry.js +7 -3
- package/dist/systems/wall/wall-materials.d.ts.map +1 -1
- package/dist/systems/wall/wall-materials.js +28 -35
- package/dist/systems/wall/wall-system.d.ts +11 -0
- package/dist/systems/wall/wall-system.d.ts.map +1 -1
- package/dist/systems/wall/wall-system.js +86 -25
- package/dist/systems/wall/wall-t-junctions.d.ts +15 -0
- package/dist/systems/wall/wall-t-junctions.d.ts.map +1 -0
- package/dist/systems/wall/wall-t-junctions.js +149 -0
- package/dist/systems/window/window-animation-system.d.ts +3 -12
- package/dist/systems/window/window-animation-system.d.ts.map +1 -1
- package/dist/systems/window/window-animation-system.js +37 -122
- package/dist/systems/window/window-pose.d.ts +31 -0
- package/dist/systems/window/window-pose.d.ts.map +1 -0
- package/dist/systems/window/window-pose.js +152 -0
- package/dist/systems/window/window-roller-shutter.d.ts +8 -0
- package/dist/systems/window/window-roller-shutter.d.ts.map +1 -0
- package/dist/systems/window/window-roller-shutter.js +64 -0
- package/dist/systems/window/window-system.d.ts +2 -11
- package/dist/systems/window/window-system.d.ts.map +1 -1
- package/dist/systems/window/window-system.js +470 -303
- package/package.json +6 -6
- package/dist/systems/interactive/control-widget.d.ts +0 -10
- package/dist/systems/interactive/control-widget.d.ts.map +0 -1
- package/dist/systems/interactive/control-widget.js +0 -35
- package/dist/systems/interactive/procedural-controls.d.ts +0 -13
- package/dist/systems/interactive/procedural-controls.d.ts.map +0 -1
- package/dist/systems/interactive/procedural-controls.js +0 -17
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import { useFrame, useThree } from '@react-three/fiber';
|
|
2
2
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
-
import { Color, Layers, Matrix4, Scene, UnsignedByteType } from 'three';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
3
|
+
import { ACESFilmicToneMapping, Color, Layers, Matrix4, NeutralToneMapping, Scene, UnsignedByteType, Vector3, } from 'three';
|
|
4
|
+
import { bloom } from 'three/addons/tsl/display/BloomNode.js';
|
|
5
|
+
import { fxaa } from 'three/addons/tsl/display/FXAANode.js';
|
|
6
|
+
import { ao as gtao } from 'three/addons/tsl/display/GTAONode.js';
|
|
7
|
+
import { traa } from 'three/addons/tsl/display/TRAANode.js';
|
|
8
|
+
import { add, diffuseColor, ivec2, mix, normalView, normalWorldGeometry, oscSine, output, pass, premultiplyAlpha, renderOutput, sample, saturation, screenUV, smoothstep, time, color as tslColor, float as tslFloat, length as tslLength, uniform, vec3, vec4, velocity, } from 'three/tsl';
|
|
7
9
|
import { RenderPipeline, TimestampQuery } from 'three/webgpu';
|
|
8
10
|
import { backdropColors, backdropGradient, flatBackdropOutput } from '../../lib/backdrop';
|
|
11
|
+
import { DAYLIGHT_BLOOM, DAYLIGHT_GRADE, DAYLIGHT_INK_BACKDROP, daylightGradeRgb, isDaylightAtmosphere as isDaylight, } from '../../lib/daylight/look';
|
|
12
|
+
import { daylightRoomContext } from '../../lib/daylight/room-context';
|
|
9
13
|
import { edgeColorFor, edgeOpacityScaleFor } from '../../lib/edge-style';
|
|
10
14
|
import { PERF_OVERLAY_ENABLED } from '../../lib/gpu-perf';
|
|
11
15
|
import { createEdgeDepthSampler, inkedEdges } from '../../lib/ink-edges';
|
|
@@ -16,18 +20,31 @@ import { isLiveFrameHeld } from '../../lib/live-frame-hold';
|
|
|
16
20
|
import { mergedOutline } from '../../lib/merged-outline-node';
|
|
17
21
|
import { recordPerfSample, timeSpan } from '../../lib/perf-tracks';
|
|
18
22
|
import { PostProcessingResources } from '../../lib/post-processing-resources';
|
|
23
|
+
import { renderWithSceneMatrices } from '../../lib/scene-matrices';
|
|
19
24
|
import { sceneLightingMask, scenePassMrt } from '../../lib/scene-pass-mrt';
|
|
20
25
|
import { packNormalToRGB, unpackRGBToNormal } from '../../lib/tsl-compat';
|
|
21
26
|
import { earthNow, UNDERGROUND_VIGNETTE, undergroundAmbianceNode, undergroundBackdrop, undergroundDissolve, undergroundDissolveGrain, undergroundEarthNode, undergroundFogAmount, undergroundVignette, worldPositionFromDepth, } from '../../lib/underground-ambiance';
|
|
22
27
|
import useViewer from '../../store/use-viewer';
|
|
23
28
|
import { useSceneAtmosphere } from './scene-atmosphere';
|
|
29
|
+
/** GTAO over the whole image (enhanced viewer's `gtao-whole`: strength 0.6 × 0.8). */
|
|
30
|
+
const AO_BLEND = 0.48;
|
|
31
|
+
const NEIGHBOURS_3X3 = [
|
|
32
|
+
[-1, -1],
|
|
33
|
+
[0, -1],
|
|
34
|
+
[1, -1],
|
|
35
|
+
[-1, 0],
|
|
36
|
+
[1, 0],
|
|
37
|
+
[-1, 1],
|
|
38
|
+
[0, 1],
|
|
39
|
+
[1, 1],
|
|
40
|
+
];
|
|
24
41
|
// A mild mid-gray contrast and saturation lift separates surfaces before the
|
|
25
|
-
// output transform. Saved project looks can turn it off.
|
|
42
|
+
// output transform. Saved project looks can turn it off.
|
|
26
43
|
export const GRADE_PARAMS = {
|
|
27
44
|
contrast: 1.05,
|
|
28
45
|
saturation: 1.1,
|
|
29
46
|
};
|
|
30
|
-
// SSGI
|
|
47
|
+
// SSGI parameters for the snapshot pipeline's ambient occlusion.
|
|
31
48
|
export const SSGI_PARAMS = {
|
|
32
49
|
enabled: true,
|
|
33
50
|
sliceCount: 1,
|
|
@@ -48,8 +65,8 @@ export const SSGI_PARAMS = {
|
|
|
48
65
|
// allocation + per-frame work for that stage, so device temperature deltas
|
|
49
66
|
// across combos isolate which pass is the actual culprit. Picked up once at
|
|
50
67
|
// pipeline build; reload after changing the URL.
|
|
51
|
-
// - ao: skip
|
|
52
|
-
// - denoise: keep SSGI but feed its raw noisy AO straight to the composite
|
|
68
|
+
// - ao: skip ambient occlusion entirely
|
|
69
|
+
// - denoise: (snapshots) keep SSGI but feed its raw noisy AO straight to the composite
|
|
53
70
|
// - outline: skip the merged-outline node and its 14 internal RTs
|
|
54
71
|
// - postFx: bypass the whole RenderPipeline and use renderer.render(scene, camera)
|
|
55
72
|
// directly — isolates raw scene-render cost from any post-FX overhead
|
|
@@ -218,13 +235,18 @@ const PostProcessingPasses = ({ hoverStyles = DEFAULT_HOVER_STYLES, disablePostF
|
|
|
218
235
|
const hoverPulseMix = useMemo(() => uniform(DEFAULT_HOVER_STYLE.pulse ? 0 : 1), []);
|
|
219
236
|
// Subscribe to projectId so the pipeline rebuilds on project switch
|
|
220
237
|
const projectId = useViewer((s) => s.projectId);
|
|
221
|
-
const shading = useViewer((s) => s.shading);
|
|
222
238
|
const ambientOcclusion = useViewer((s) => s.sceneAppearance.postprocessing?.ambientOcclusion !== false);
|
|
223
239
|
const colorGrading = useViewer((s) => s.sceneAppearance.postprocessing?.colorGrading !== false);
|
|
224
240
|
const flatBackground = useViewer((s) => s.sceneAppearance.backgroundSky === null);
|
|
225
241
|
const edges = useViewer((s) => s.edges);
|
|
226
242
|
const inkOpacityOverride = useViewer((s) => s.inkOpacity);
|
|
227
243
|
const transparentBackground = useViewer((s) => s.transparentBackground);
|
|
244
|
+
const renderQuality = useViewer((s) => s.renderQuality);
|
|
245
|
+
// Daylight look uniforms, refreshed every frame from the atmosphere/exposure.
|
|
246
|
+
const whiteBalance = useMemo(() => uniform(new Vector3(1, 1, 1)), []);
|
|
247
|
+
// 1 / exposure: the bloom threshold (only display-white blooms) and the daylight look's
|
|
248
|
+
// compensation of unlit overlays (zones, editor overlays), which keep their designed brightness.
|
|
249
|
+
const exposureInverse = useMemo(() => uniform(1), []);
|
|
228
250
|
const lastProjectIdRef = useRef(projectId);
|
|
229
251
|
// Bump this to force a pipeline rebuild (used by retry logic)
|
|
230
252
|
const [pipelineVersion, setPipelineVersion] = useState(0);
|
|
@@ -305,24 +327,33 @@ const PostProcessingPasses = ({ hoverStyles = DEFAULT_HOVER_STYLES, disablePostF
|
|
|
305
327
|
disposePipeline();
|
|
306
328
|
return;
|
|
307
329
|
}
|
|
308
|
-
|
|
309
|
-
|
|
330
|
+
// Quality tiers (both appearances; the enhanced-viewer reference):
|
|
331
|
+
// high — full-res GTAO from the scene pass, soft whole-image blend, TRAA, bloom (daylight)
|
|
332
|
+
// medium — ½-res GTAO from the scene pass, soft whole-image blend, TRAA
|
|
333
|
+
// (The reference's high tier fed GTAO from a depth/normal pre-pass, darkening indirect light
|
|
334
|
+
// only; a pre-pass draws the whole scene twice, which an editor scene of thousands of meshes
|
|
335
|
+
// can't afford.)
|
|
336
|
+
// low — no AO, FXAA
|
|
337
|
+
// The colored appearance adds the daylight look: Neutral tone mapping, white balance, grade
|
|
338
|
+
// and a display-space vignette. Ink edges belong to the monochrome appearance only.
|
|
339
|
+
const daylight = isDaylight(atmosphere);
|
|
340
|
+
renderer.toneMapping = daylight ? NeutralToneMapping : ACESFilmicToneMapping;
|
|
341
|
+
const aoTier = ambientOcclusion && !perfDisable.ao && renderQuality !== 'low' ? renderQuality : null;
|
|
342
|
+
const temporalAA = !transparentBackground && renderQuality !== 'low';
|
|
343
|
+
const fxaaEnabled = !transparentBackground && renderQuality === 'low';
|
|
344
|
+
const bloomEnabled = daylight && !transparentBackground && renderQuality === 'high';
|
|
310
345
|
const outlineEnabled = !perfDisable.outline;
|
|
311
|
-
const inkEnabled = edges !== 'off';
|
|
312
|
-
const needsNormalMRT =
|
|
313
|
-
//
|
|
314
|
-
// (2px, ~2× wider detected band) + solid (100%). The edge masks saturate,
|
|
315
|
-
// so radius+opacity are what actually separate the two modes.
|
|
346
|
+
const inkEnabled = !daylight && edges !== 'off';
|
|
347
|
+
const needsNormalMRT = inkEnabled || aoTier !== null || temporalAA;
|
|
348
|
+
// Ink: thin (1px sample radius) and faint (50% opacity).
|
|
316
349
|
const inkRadius = 1;
|
|
317
|
-
const inkOpacity = inkOpacityOverride ??
|
|
350
|
+
const inkOpacity = inkOpacityOverride ?? 0.5;
|
|
318
351
|
console.log('[viewer/post-processing] Building pipeline', {
|
|
319
352
|
version: pipelineVersion,
|
|
320
|
-
|
|
321
|
-
denoise: denoiseEnabled,
|
|
353
|
+
quality: renderQuality,
|
|
322
354
|
outline: outlineEnabled,
|
|
323
355
|
perfDisable,
|
|
324
356
|
projectId,
|
|
325
|
-
shading,
|
|
326
357
|
ambientOcclusion,
|
|
327
358
|
colorGrading,
|
|
328
359
|
transparentBackground,
|
|
@@ -331,7 +362,7 @@ const PostProcessingPasses = ({ hoverStyles = DEFAULT_HOVER_STYLES, disablePostF
|
|
|
331
362
|
height,
|
|
332
363
|
});
|
|
333
364
|
hasPipelineErrorRef.current = false;
|
|
334
|
-
// WebGPU availability check:
|
|
365
|
+
// WebGPU availability check: GTAO, TRAA, and RenderPipeline are all
|
|
335
366
|
// WebGPU-only APIs. When the browser falls back to WebGL2 (no
|
|
336
367
|
// `navigator.gpu`, or the device couldn't be created), building the
|
|
337
368
|
// pipeline either throws silently or produces a broken output where
|
|
@@ -357,14 +388,17 @@ const PostProcessingPasses = ({ hoverStyles = DEFAULT_HOVER_STYLES, disablePostF
|
|
|
357
388
|
try {
|
|
358
389
|
const layerIndex = new LayerPassIndex(scene, [ZONE_LAYER, OVERLAY_LAYER]);
|
|
359
390
|
resources.layerIndex = layerIndex;
|
|
360
|
-
|
|
391
|
+
// Temporal AA / FXAA replace MSAA, and the ½-res GTAO gathers from this depth (no multisampled
|
|
392
|
+
// `textureGather`): both want a single-sampled scene pass.
|
|
393
|
+
const singleSampled = temporalAA || fxaaEnabled || aoTier !== null;
|
|
394
|
+
const scenePass = pass(scene, camera, singleSampled ? { samples: 0 } : undefined);
|
|
361
395
|
resources.passes.push(scenePass);
|
|
362
396
|
scenePass.setLayers(sceneOnlyLayers);
|
|
363
397
|
const zonePass = new LayerPassNode(layerIndex, camera, ZONE_LAYER, scenePass);
|
|
364
398
|
resources.passes.push(zonePass);
|
|
365
399
|
zonePass.setLayers(zoneLayers);
|
|
366
400
|
// Editor overlays (gizmos, move handles, tool previews, grid) on their own
|
|
367
|
-
// layer, kept out of the depth/normal MRT above so the ink +
|
|
401
|
+
// layer, kept out of the depth/normal MRT above so the ink + AO ignore
|
|
368
402
|
// them, then composited on top of the final image below.
|
|
369
403
|
const overlayPass = new LayerPassNode(layerIndex, camera, OVERLAY_LAYER, scenePass);
|
|
370
404
|
resources.passes.push(overlayPass);
|
|
@@ -378,11 +412,12 @@ const PostProcessingPasses = ({ hoverStyles = DEFAULT_HOVER_STYLES, disablePostF
|
|
|
378
412
|
const hasGeometry = scenePassColor.a;
|
|
379
413
|
const contentAlpha = hasGeometry.max(zonePass.a);
|
|
380
414
|
// Composite the zone-pass tint into the base scene so rooms show whether or
|
|
381
|
-
// not
|
|
382
|
-
// with
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
//
|
|
415
|
+
// not AO is enabled. The AO branch below rebuilds this
|
|
416
|
+
// composite with the zone tint kept out of the AO (no double-add).
|
|
417
|
+
const zoneRgb = daylight ? zonePass.rgb.mul(exposureInverse) : zonePass.rgb;
|
|
418
|
+
let sceneColor = vec4(add(scenePassColor.rgb, zoneRgb), contentAlpha);
|
|
419
|
+
// Scene depth is shared by AO, ink, and outlines.
|
|
420
|
+
// The normal MRT is only built when the medium AO, TRAA or ink needs it.
|
|
386
421
|
const scenePassDepth = scenePass.getTextureNode('depth');
|
|
387
422
|
const sampleEdgeDepth = createEdgeDepthSampler(scenePassDepth, camera);
|
|
388
423
|
let scenePassNormal = null;
|
|
@@ -392,6 +427,7 @@ const PostProcessingPasses = ({ hoverStyles = DEFAULT_HOVER_STYLES, disablePostF
|
|
|
392
427
|
diffuseColor,
|
|
393
428
|
normal: packNormalToRGB(normalView),
|
|
394
429
|
lighting: sceneLightingMask(),
|
|
430
|
+
...(temporalAA ? { velocity } : {}),
|
|
395
431
|
}));
|
|
396
432
|
scenePassNormal = scenePass.getTextureNode('normal');
|
|
397
433
|
}
|
|
@@ -400,56 +436,32 @@ const PostProcessingPasses = ({ hoverStyles = DEFAULT_HOVER_STYLES, disablePostF
|
|
|
400
436
|
normalTexture.type = UnsignedByteType;
|
|
401
437
|
scenePass.getTexture('lighting').type = UnsignedByteType;
|
|
402
438
|
}
|
|
403
|
-
// Extract normal from color-encoded texture (
|
|
439
|
+
// Extract normal from color-encoded texture (GTAO consumes the node form).
|
|
404
440
|
const normalNode = scenePassNormal;
|
|
405
441
|
const sceneNormal = normalNode
|
|
406
442
|
? sample((uv) => unpackRGBToNormal(normalNode.sample(uv)))
|
|
407
443
|
: null;
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
if (denoiseEnabled) {
|
|
430
|
-
// DenoiseNode only denoises RGB — alpha is passed through unchanged.
|
|
431
|
-
// SSGI's AO is a single red channel, so we remap it into RGB before denoising.
|
|
432
|
-
const aoAsRgb = vec4(aoTexture.r, aoTexture.r, aoTexture.r, tslFloat(1));
|
|
433
|
-
const denoisePass = denoise(aoAsRgb, scenePassDepth, sceneNormal, camera);
|
|
434
|
-
denoisePass.index.value = 0;
|
|
435
|
-
denoisePass.radius.value = 4;
|
|
436
|
-
ao = denoisePass.r;
|
|
437
|
-
}
|
|
438
|
-
else {
|
|
439
|
-
// Diagnostic path: feed raw noisy SSGI AO straight through. Will
|
|
440
|
-
// look grainy — that's the point, it isolates denoise cost.
|
|
441
|
-
ao = aoTexture.r;
|
|
442
|
-
}
|
|
443
|
-
// AO is a near/mid-field cue like the ink: fade it out with raw depth
|
|
444
|
-
// (same ≈150→350 m window as ink-edges' distanceFade) so the horizon
|
|
445
|
-
// disc and the geometry↔sky depth cliff never grow an AO band — that
|
|
446
|
-
// band read as a visible line along the horizon.
|
|
447
|
-
const aoFarFade = smoothstep(tslFloat(0.9994), tslFloat(0.9998), sampleEdgeDepth(screenUV));
|
|
448
|
-
ao = mix(ao, tslFloat(1), aoFarFade);
|
|
449
|
-
const lightingWeight = scenePass.getTextureNode('lighting').r;
|
|
450
|
-
ao = mix(tslFloat(1), ao, lightingWeight);
|
|
451
|
-
// Composite: scene * AO + diffuse * GI
|
|
452
|
-
sceneColor = vec4(add(scenePassColor.rgb.mul(ao), add(zonePass.rgb, scenePassDiffuse.rgb.mul(gi).mul(lightingWeight))), contentAlpha);
|
|
444
|
+
// AO: GTAO from the scene pass's depth and normals, blended softly over the whole image
|
|
445
|
+
// (full resolution on high, ½ on medium).
|
|
446
|
+
let temporalDepth = null;
|
|
447
|
+
let temporalVelocity = null;
|
|
448
|
+
if (aoTier && sceneNormal && scenePassDepth) {
|
|
449
|
+
const aoNode = gtao(scenePassDepth, sceneNormal, camera);
|
|
450
|
+
aoNode.radius.value = 0.6;
|
|
451
|
+
aoNode.thickness.value = 1.2;
|
|
452
|
+
aoNode.distanceExponent.value = 1.4;
|
|
453
|
+
aoNode.samples.value = 16;
|
|
454
|
+
aoNode.resolutionScale = aoTier === 'high' ? 1 : 0.5;
|
|
455
|
+
resources.effects.push(aoNode);
|
|
456
|
+
const aoBlend = mix(tslFloat(1), aoNode.getTextureNode().sample(screenUV).r, AO_BLEND);
|
|
457
|
+
sceneColor = vec4(add(scenePassColor.rgb.mul(aoBlend), zoneRgb), contentAlpha);
|
|
458
|
+
}
|
|
459
|
+
// The daylight look's room light (windows, lamps, bounce, night spill).
|
|
460
|
+
if (daylight)
|
|
461
|
+
scenePass.contextNode = daylightRoomContext();
|
|
462
|
+
if (temporalAA && !temporalVelocity && scenePassNormal) {
|
|
463
|
+
temporalDepth = scenePassDepth;
|
|
464
|
+
temporalVelocity = scenePass.getTextureNode('velocity');
|
|
453
465
|
}
|
|
454
466
|
// Screen-space ink outline (SketchUp look) — depth/normal edge detection
|
|
455
467
|
// over the composited scene. Topology-agnostic, so it handles CSG-cut
|
|
@@ -464,13 +476,25 @@ const PostProcessingPasses = ({ hoverStyles = DEFAULT_HOVER_STYLES, disablePostF
|
|
|
464
476
|
opacity: tslFloat(inkOpacity).mul(inkOpacityScaleUniform.current),
|
|
465
477
|
}), sceneColor.a);
|
|
466
478
|
}
|
|
479
|
+
// Bloom on the HDR image; the threshold follows exposure so only display-white blooms.
|
|
480
|
+
if (bloomEnabled) {
|
|
481
|
+
// Editor highlights stay out of it (`markHighlight`): they must not haze over the object.
|
|
482
|
+
const bloomWeight = scenePassNormal ? scenePass.getTextureNode('lighting').g : tslFloat(1);
|
|
483
|
+
const glow = bloom(vec4(sceneColor.rgb.mul(bloomWeight), sceneColor.a), DAYLIGHT_BLOOM.strength, DAYLIGHT_BLOOM.radius, exposureInverse);
|
|
484
|
+
glow.smoothWidth.value = DAYLIGHT_BLOOM.softKnee;
|
|
485
|
+
resources.effects.push(glow);
|
|
486
|
+
sceneColor = vec4(sceneColor.rgb.add(glow.rgb), sceneColor.a);
|
|
487
|
+
}
|
|
467
488
|
// Scene-referred grade (contrast around mid-gray + saturation) before the
|
|
468
|
-
// pipeline's output tone mapping.
|
|
469
|
-
// the flat presets stay exact. The same transform is applied to the
|
|
489
|
+
// pipeline's output tone mapping. The same transform is applied to the
|
|
470
490
|
// backdrop below so geometry that fades to the background colour (the
|
|
471
|
-
// horizon disc) matches it exactly.
|
|
472
|
-
|
|
473
|
-
|
|
491
|
+
// horizon disc) matches it exactly. The daylight look adds eye-like white
|
|
492
|
+
// balance (sky and scene alike).
|
|
493
|
+
const gradeRgb = (rgb) => daylight
|
|
494
|
+
? daylightGradeRgb(rgb, whiteBalance)
|
|
495
|
+
: saturation(rgb.div(0.18).pow(vec3(GRADE_PARAMS.contrast)).mul(0.18), GRADE_PARAMS.saturation);
|
|
496
|
+
const graded = colorGrading;
|
|
497
|
+
if (graded) {
|
|
474
498
|
sceneColor = vec4(gradeRgb(sceneColor.rgb), sceneColor.a);
|
|
475
499
|
}
|
|
476
500
|
// Reused scene depth lets outlined groups occlude each other; materials
|
|
@@ -509,7 +533,9 @@ const PostProcessingPasses = ({ hoverStyles = DEFAULT_HOVER_STYLES, disablePostF
|
|
|
509
533
|
.max(outlineNode.secondaryVisibleEdge)
|
|
510
534
|
.max(outlineNode.secondaryHiddenEdge);
|
|
511
535
|
visualAlpha = visualAlpha.max(outlineAlpha);
|
|
512
|
-
compositeWithOutlines = vec4(add(sceneColor.rgb,
|
|
536
|
+
compositeWithOutlines = vec4(add(sceneColor.rgb, daylight
|
|
537
|
+
? selectedOutline.add(hoverOutline).mul(exposureInverse)
|
|
538
|
+
: selectedOutline.add(hoverOutline)), sceneColor.a);
|
|
513
539
|
}
|
|
514
540
|
// Backdrop: world-space view ray per pixel → background / horizon haze /
|
|
515
541
|
// sky gradient (shared formula in lib/backdrop.ts). The horizon disc
|
|
@@ -527,7 +553,7 @@ const PostProcessingPasses = ({ hoverStyles = DEFAULT_HOVER_STYLES, disablePostF
|
|
|
527
553
|
sky: bgSkyUniform.current,
|
|
528
554
|
skyDeep: bgSkyDeepUniform.current,
|
|
529
555
|
});
|
|
530
|
-
if (
|
|
556
|
+
if (graded) {
|
|
531
557
|
bgGradient = gradeRgb(bgGradient);
|
|
532
558
|
}
|
|
533
559
|
let sceneComposite = compositeWithOutlines.rgb;
|
|
@@ -554,14 +580,35 @@ const PostProcessingPasses = ({ hoverStyles = DEFAULT_HOVER_STYLES, disablePostF
|
|
|
554
580
|
sceneComposite = mix(sceneComposite, dark, undergroundFogAmount(worldPosition).mul(dive));
|
|
555
581
|
}
|
|
556
582
|
let composited = mix(bgGradient, sceneComposite, contentAlpha);
|
|
583
|
+
// Temporal AA resolves the whole picture, so silhouettes against the sky are smoothed too.
|
|
584
|
+
// Its history is only valid where the 3×3 neighbourhood holds geometry (open sky, at the far
|
|
585
|
+
// plane, resolves to black), so the sky away from geometry keeps the direct backdrop.
|
|
586
|
+
if (temporalAA && temporalVelocity && temporalDepth) {
|
|
587
|
+
const temporal = traa(vec4(composited, 1), temporalDepth, temporalVelocity, camera);
|
|
588
|
+
resources.effects.push(temporal);
|
|
589
|
+
let nearGeometry = contentAlpha;
|
|
590
|
+
for (const [x, y] of NEIGHBOURS_3X3) {
|
|
591
|
+
nearGeometry = nearGeometry.max(scenePassColor.offset(ivec2(x, y)).a);
|
|
592
|
+
}
|
|
593
|
+
composited = mix(composited, temporal.rgb, nearGeometry.greaterThan(0).select(1, 0));
|
|
594
|
+
}
|
|
557
595
|
// The screen's edges close in with it.
|
|
558
596
|
if (dive) {
|
|
559
597
|
composited = mix(composited, tslColor(UNDERGROUND_VIGNETTE), undergroundVignette().mul(dive));
|
|
560
598
|
}
|
|
561
|
-
//
|
|
562
|
-
//
|
|
563
|
-
|
|
564
|
-
|
|
599
|
+
// Display-space picture: tone mapping + colour space, then the daylight vignette (after the
|
|
600
|
+
// tone mapping, as in the reference). Editor overlays are painted on top by their own alpha —
|
|
601
|
+
// they never get inked, AO'd, outlined or vignetted, and always read crisp.
|
|
602
|
+
const toDisplay = (rgb) => renderOutput(vec4(rgb, 1), renderer.toneMapping, renderer.outputColorSpace);
|
|
603
|
+
let displayScene = toDisplay(composited);
|
|
604
|
+
if (daylight) {
|
|
605
|
+
const vignette = tslLength(screenUV.sub(0.5))
|
|
606
|
+
.mul(1.35)
|
|
607
|
+
.pow(2.5)
|
|
608
|
+
.mul(DAYLIGHT_GRADE.vignette);
|
|
609
|
+
displayScene = vec4(displayScene.rgb.mul(tslFloat(1).sub(vignette)), 1);
|
|
610
|
+
}
|
|
611
|
+
let finalOutput = vec4(mix(displayScene.rgb, toDisplay(daylight ? overlayColor.rgb.mul(exposureInverse) : overlayColor.rgb).rgb, overlayColor.a), tslFloat(1));
|
|
565
612
|
if (transparentBackground) {
|
|
566
613
|
const overlayAlpha = overlayColor.a;
|
|
567
614
|
const alpha = overlayAlpha.add(visualAlpha.mul(overlayAlpha.oneMinus()));
|
|
@@ -582,10 +629,13 @@ const PostProcessingPasses = ({ hoverStyles = DEFAULT_HOVER_STYLES, disablePostF
|
|
|
582
629
|
const overlay = renderOutput(vec4(overlayColor.rgb, 1), renderer.toneMapping, renderer.outputColorSpace);
|
|
583
630
|
finalOutput = vec4(mix(backdrop.rgb, overlay.rgb, overlayColor.a), 1);
|
|
584
631
|
}
|
|
632
|
+
// Low tier: FXAA on the display image (no temporal AA, no MSAA).
|
|
633
|
+
if (fxaaEnabled)
|
|
634
|
+
finalOutput = fxaa(finalOutput);
|
|
585
635
|
const renderPipeline = new RenderPipeline(renderer);
|
|
586
636
|
resources.pipeline = renderPipeline;
|
|
587
|
-
|
|
588
|
-
|
|
637
|
+
// Every branch above ends in display space.
|
|
638
|
+
renderPipeline.outputColorTransform = false;
|
|
589
639
|
renderPipeline.outputNode = finalOutput;
|
|
590
640
|
retryCountRef.current = 0;
|
|
591
641
|
}
|
|
@@ -593,7 +643,6 @@ const PostProcessingPasses = ({ hoverStyles = DEFAULT_HOVER_STYLES, disablePostF
|
|
|
593
643
|
hasPipelineErrorRef.current = true;
|
|
594
644
|
console.error('[viewer/post-processing] Failed to set up post-processing pipeline. Rendering without post FX.', {
|
|
595
645
|
version: pipelineVersion,
|
|
596
|
-
ssgi: SSGI_PARAMS.enabled,
|
|
597
646
|
rendererCtor: renderer.constructor?.name,
|
|
598
647
|
}, error);
|
|
599
648
|
disposePipeline();
|
|
@@ -618,11 +667,13 @@ const PostProcessingPasses = ({ hoverStyles = DEFAULT_HOVER_STYLES, disablePostF
|
|
|
618
667
|
projectId,
|
|
619
668
|
renderer,
|
|
620
669
|
scene,
|
|
621
|
-
shading,
|
|
622
670
|
ambientOcclusion,
|
|
623
671
|
colorGrading,
|
|
624
672
|
flatBackground,
|
|
625
673
|
transparentBackground,
|
|
674
|
+
renderQuality,
|
|
675
|
+
whiteBalance,
|
|
676
|
+
exposureInverse,
|
|
626
677
|
size.height,
|
|
627
678
|
size.width,
|
|
628
679
|
zoneLayers,
|
|
@@ -669,10 +720,16 @@ const PostProcessingPasses = ({ hoverStyles = DEFAULT_HOVER_STYLES, disablePostF
|
|
|
669
720
|
bgSkyDeepTarget.current.set(backdrop.skyDeep);
|
|
670
721
|
bgSkyDeepCurrent.current.lerp(bgSkyDeepTarget.current, Math.min(delta, 0.1) * 4);
|
|
671
722
|
bgSkyDeepUniform.current.value.copy(bgSkyDeepCurrent.current);
|
|
723
|
+
if (isDaylight(atmosphere))
|
|
724
|
+
whiteBalance.value.copy(atmosphere.whiteBalance);
|
|
725
|
+
exposureInverse.value = 1 / Math.max(renderer.toneMappingExposure, 1e-3);
|
|
672
726
|
camProjInvUniform.current.value.copy(camera.projectionMatrixInverse);
|
|
673
727
|
camWorldUniform.current.value.copy(camera.matrixWorld);
|
|
674
|
-
// Ink colour follows the (lerping) background luminance — snaps dark↔light.
|
|
675
|
-
|
|
728
|
+
// Ink colour follows the (lerping) background luminance — snaps dark↔light. The daylight
|
|
729
|
+
// look ignores the scene theme: its edges are always the light-backdrop ink.
|
|
730
|
+
const bgHex = isDaylight(atmosphere)
|
|
731
|
+
? DAYLIGHT_INK_BACKDROP
|
|
732
|
+
: `#${bgCurrent.current.getHexString()}`;
|
|
676
733
|
inkColorUniform.current.value.set(edgeColorFor(bgHex));
|
|
677
734
|
inkOpacityScaleUniform.current.value = edgeOpacityScaleFor(bgHex);
|
|
678
735
|
const outliner = useViewer.getState().outliner;
|
|
@@ -703,8 +760,7 @@ const PostProcessingPasses = ({ hoverStyles = DEFAULT_HOVER_STYLES, disablePostF
|
|
|
703
760
|
}
|
|
704
761
|
const submittedAt = PERF_OVERLAY_ENABLED ? performance.now() : 0;
|
|
705
762
|
timeSpan('render-encode', () => {
|
|
706
|
-
;
|
|
707
|
-
renderer.render(scene, camera);
|
|
763
|
+
renderWithSceneMatrices(scene, () => renderer.render(scene, camera));
|
|
708
764
|
});
|
|
709
765
|
if (PERF_OVERLAY_ENABLED)
|
|
710
766
|
recordFrameGpuTiming(renderer, submittedAt);
|
|
@@ -726,7 +782,7 @@ const PostProcessingPasses = ({ hoverStyles = DEFAULT_HOVER_STYLES, disablePostF
|
|
|
726
782
|
renderer.setClearAlpha(0);
|
|
727
783
|
const submittedAt = PERF_OVERLAY_ENABLED ? performance.now() : 0;
|
|
728
784
|
timeSpan('render-encode', () => {
|
|
729
|
-
pipeline.render();
|
|
785
|
+
renderWithSceneMatrices(scene, () => pipeline.render());
|
|
730
786
|
});
|
|
731
787
|
if (PERF_OVERLAY_ENABLED)
|
|
732
788
|
recordFrameGpuTiming(renderer, submittedAt);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registered-systems.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/registered-systems.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"registered-systems.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/registered-systems.tsx"],"names":[],"mappings":"AAqDA;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,iBAAiB,uCAkChC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import { createSceneApi, isNodeKindEnabled, nodeRegistry, useRegistryVersion, useScene, } from '@pascal-app/core';
|
|
3
|
+
import { createSceneApi, isDevelopmentBuild, isNodeKindEnabled, nodeRegistry, useRegistryVersion, useScene, } from '@pascal-app/core';
|
|
4
4
|
import { Component, lazy, Suspense, useMemo } from 'react';
|
|
5
5
|
const DEFAULT_PRIORITY = 5;
|
|
6
6
|
const lazyCache = new WeakMap();
|
|
@@ -20,7 +20,7 @@ class SystemErrorBoundary extends Component {
|
|
|
20
20
|
return { failed: true };
|
|
21
21
|
}
|
|
22
22
|
componentDidCatch(error) {
|
|
23
|
-
if (
|
|
23
|
+
if (isDevelopmentBuild()) {
|
|
24
24
|
console.error(`[viewer] Disabled system ${this.props.systemId}`, error);
|
|
25
25
|
}
|
|
26
26
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { useThree } from '@react-three/fiber';
|
|
3
|
-
import { useEffect
|
|
3
|
+
import { useEffect } from 'react';
|
|
4
4
|
import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js';
|
|
5
5
|
import * as THREE from 'three/webgpu';
|
|
6
6
|
import useViewer from '../../store/use-viewer';
|
|
@@ -68,8 +68,6 @@ export function SceneEnvironment() {
|
|
|
68
68
|
const environment = useViewer((state) => state.sceneAppearance.environment);
|
|
69
69
|
const source = environment?.source ?? 'gradient';
|
|
70
70
|
const atmosphere = useSceneAtmosphere();
|
|
71
|
-
const atmosphereRef = useRef(atmosphere);
|
|
72
|
-
atmosphereRef.current = atmosphere;
|
|
73
71
|
useEffect(() => {
|
|
74
72
|
const prevEnvironment = scene.environment;
|
|
75
73
|
let target;
|
|
@@ -104,9 +102,10 @@ export function SceneEnvironment() {
|
|
|
104
102
|
const previous = scene.environmentIntensity;
|
|
105
103
|
scene.environmentIntensity =
|
|
106
104
|
environment?.intensity ?? (appearance === 'dark' ? ENV_INTENSITY_DARK : ENV_INTENSITY);
|
|
105
|
+
// Always hand back what was there: when an atmosphere arrives after this ran (its owner
|
|
106
|
+
// installs in a layout effect, before this component sees it), that is the atmosphere's 1.
|
|
107
107
|
return () => {
|
|
108
|
-
|
|
109
|
-
scene.environmentIntensity = previous;
|
|
108
|
+
scene.environmentIntensity = previous;
|
|
110
109
|
};
|
|
111
110
|
}, [appearance, atmosphere, scene, environment?.intensity]);
|
|
112
111
|
return null;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { type InteractionSound, type MessageDescriptor } from '@pascal-app/core';
|
|
2
|
+
import { type Object3D, Vector3 } from 'three';
|
|
3
|
+
export type CalloutIcon = 'door' | 'window' | 'shutters' | 'light' | 'fan' | 'power';
|
|
4
|
+
export type CalloutSound = {
|
|
5
|
+
on: InteractionSound;
|
|
6
|
+
off: InteractionSound;
|
|
7
|
+
swing?: InteractionSound;
|
|
8
|
+
};
|
|
9
|
+
/** Scene data (a name, a part's label) as written, or UI copy to translate. */
|
|
10
|
+
export type CalloutText = string | MessageDescriptor;
|
|
11
|
+
/** One control on the callout: a pill that flips something, or a pill holding a range. */
|
|
12
|
+
export type CalloutAction = {
|
|
13
|
+
kind: 'toggle';
|
|
14
|
+
key: string;
|
|
15
|
+
icon: CalloutIcon;
|
|
16
|
+
on: boolean;
|
|
17
|
+
/** The element or part it acts on, for the spoken label. */
|
|
18
|
+
name: string;
|
|
19
|
+
/** A part's own name on the pill; without it the pill shows its state word. */
|
|
20
|
+
text?: CalloutText;
|
|
21
|
+
/** `swing` sounds as a closing run starts, `off` as it lands. */
|
|
22
|
+
sound: CalloutSound;
|
|
23
|
+
/** When closing returns a promise for the end of the run, the closing sound waits for it. */
|
|
24
|
+
toggle: () => void | Promise<void>;
|
|
25
|
+
} | {
|
|
26
|
+
kind: 'range';
|
|
27
|
+
key: string;
|
|
28
|
+
label: string;
|
|
29
|
+
value: number;
|
|
30
|
+
min: number;
|
|
31
|
+
max: number;
|
|
32
|
+
step: number;
|
|
33
|
+
unit?: string;
|
|
34
|
+
onChange: (value: number) => void;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* The selected element's label: a dot on it, a hairline rising from the dot,
|
|
38
|
+
* then its name, a detail line, its controls and a close button. It follows
|
|
39
|
+
* the element's world point each frame rather than living inside its object:
|
|
40
|
+
* kinds that rebuild their children (a window opening) would drop it.
|
|
41
|
+
*/
|
|
42
|
+
export declare function SelectionCallout({ object, anchor: anchorProp, title, subtitle, actions, onDismiss, }: {
|
|
43
|
+
object: Object3D;
|
|
44
|
+
/** Where the dot sits, in the object's space; the centre of its bounds by default. */
|
|
45
|
+
anchor?: Vector3;
|
|
46
|
+
title: string;
|
|
47
|
+
subtitle?: CalloutText | null;
|
|
48
|
+
actions: CalloutAction[];
|
|
49
|
+
onDismiss: () => void;
|
|
50
|
+
}): import("react").JSX.Element;
|
|
51
|
+
//# sourceMappingURL=selection-callout.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selection-callout.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/selection-callout.tsx"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EAGvB,MAAM,kBAAkB,CAAA;AAYzB,OAAO,EAAqB,KAAK,QAAQ,EAAE,OAAO,EAAE,MAAM,OAAO,CAAA;AAEjE,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,QAAQ,GAAG,UAAU,GAAG,OAAO,GAAG,KAAK,GAAG,OAAO,CAAA;AAEpF,MAAM,MAAM,YAAY,GAAG;IAAE,EAAE,EAAE,gBAAgB,CAAC;IAAC,GAAG,EAAE,gBAAgB,CAAC;IAAC,KAAK,CAAC,EAAE,gBAAgB,CAAA;CAAE,CAAA;AAEpG,+EAA+E;AAC/E,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,iBAAiB,CAAA;AAEpD,0FAA0F;AAC1F,MAAM,MAAM,aAAa,GACrB;IACE,IAAI,EAAE,QAAQ,CAAA;IACd,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,WAAW,CAAA;IACjB,EAAE,EAAE,OAAO,CAAA;IACX,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAA;IACZ,+EAA+E;IAC/E,IAAI,CAAC,EAAE,WAAW,CAAA;IAClB,iEAAiE;IACjE,KAAK,EAAE,YAAY,CAAA;IACnB,6FAA6F;IAC7F,MAAM,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACnC,GACD;IACE,IAAI,EAAE,OAAO,CAAA;IACb,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CAClC,CAAA;AAgBL;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,MAAM,EACN,MAAM,EAAE,UAAU,EAClB,KAAK,EACL,QAAQ,EACR,OAAO,EACP,SAAS,GACV,EAAE;IACD,MAAM,EAAE,QAAQ,CAAA;IAChB,sFAAsF;IACtF,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,WAAW,GAAG,IAAI,CAAA;IAC7B,OAAO,EAAE,aAAa,EAAE,CAAA;IACxB,SAAS,EAAE,MAAM,IAAI,CAAA;CACtB,+BAkHA"}
|