@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,9 +1,10 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { nodeRegistry, RoofElevationSystem, StairOpeningSystem, sceneRegistry, useInteractive, useScene, } from '@pascal-app/core';
|
|
3
|
+
import { isDevelopmentBuild, nodeRegistry, RoofElevationSystem, StairOpeningSystem, sceneRegistry, useInteractive, useScene, } from '@pascal-app/core';
|
|
4
4
|
import { Canvas, extend, useFrame, useThree } from '@react-three/fiber';
|
|
5
5
|
import { forwardRef, useEffect, useImperativeHandle, useLayoutEffect, useRef, useState, } from 'react';
|
|
6
6
|
import * as THREE from 'three/webgpu';
|
|
7
|
+
import { classifyDevice, isSoftwareRenderer, readDeviceSignals } from '../../lib/device-tier';
|
|
7
8
|
import { hasDrawableGeometry } from '../../lib/drawable-geometry';
|
|
8
9
|
import { PERF_OVERLAY_ENABLED } from '../../lib/gpu-perf';
|
|
9
10
|
import { applyIsolation, clearIsolation } from '../../lib/isolation';
|
|
@@ -12,6 +13,7 @@ import { choosePointerEvents } from '../../lib/pointer-events';
|
|
|
12
13
|
import { initializeGpuRenderer } from '../../lib/renderer-capability';
|
|
13
14
|
import { getSceneTheme } from '../../lib/scene-themes';
|
|
14
15
|
import { installTextureNodeNullGuard } from '../../lib/texture-node-guard';
|
|
16
|
+
import { viewerPreferenceStorage } from '../../lib/viewer-preference-storage';
|
|
15
17
|
import useViewer, {} from '../../store/use-viewer';
|
|
16
18
|
import { FloorElevationSystem } from '../../systems/floor-elevation/floor-elevation-system';
|
|
17
19
|
import { GeometrySystem } from '../../systems/geometry/geometry-system';
|
|
@@ -21,6 +23,7 @@ import { subscribeWallBuildInteractions } from '../../systems/wall/wall-build-li
|
|
|
21
23
|
import { ImmersiveXRPresentationProvider } from '../../xr/presentation-context';
|
|
22
24
|
import { SceneRenderer } from '../renderers/scene-renderer';
|
|
23
25
|
import { BATCH_SPIKE_ENABLED, BatchedMeshSpike } from './batched-mesh-spike';
|
|
26
|
+
import { DaylightAtmosphere } from './daylight-atmosphere';
|
|
24
27
|
import FrameLimiter from './frame-limiter';
|
|
25
28
|
import { Lights } from './lights';
|
|
26
29
|
import { PerfMonitor } from './perf-monitor';
|
|
@@ -66,7 +69,7 @@ const DIRTY_BUILD_KINDS = new Set([
|
|
|
66
69
|
'wall',
|
|
67
70
|
'window',
|
|
68
71
|
]);
|
|
69
|
-
const warnedEmptyDraw =
|
|
72
|
+
const warnedEmptyDraw = isDevelopmentBuild() ? new WeakSet() : null;
|
|
70
73
|
/**
|
|
71
74
|
* Renderer-level safety net against the empty-vertex-buffer crash.
|
|
72
75
|
*
|
|
@@ -283,7 +286,13 @@ function SceneReadyTracker({ onSceneReadyChange, sceneReadyKey, sceneReadyMaxWai
|
|
|
283
286
|
}, 10);
|
|
284
287
|
return null;
|
|
285
288
|
}
|
|
286
|
-
const
|
|
289
|
+
const MAX_DPR = { high: 1.5, medium: 1.25, low: 1 };
|
|
290
|
+
/**
|
|
291
|
+
* A software renderer can't reach the usual cadence, and chasing it starves the app of the CPU it
|
|
292
|
+
* rasterizes on: frames are capped lower there.
|
|
293
|
+
*/
|
|
294
|
+
const SOFTWARE_MAX_FPS = 20;
|
|
295
|
+
const Viewer = forwardRef(function Viewer({ children, hoverStyles = DEFAULT_HOVER_STYLES, selectionManager = 'default', perf = false, useBvh = true, renderContext = 'editor', transparent, defaultRender, presentation: suppliedPresentation, daylight = true, isolate, sceneReadyKey, onSceneReadyChange, onRenderError, onBreadcrumb, sceneReadyMaxWaitMs, maxFps = 50, disablePostFx = false, renderPaused = false, immersive, }, ref) {
|
|
287
296
|
useEffect(() => {
|
|
288
297
|
if (nodeRegistry.size === 0) {
|
|
289
298
|
console.warn('[viewer] Node registry is empty. Install @pascal-app/nodes and call await loadPlugin(builtinPlugin) before mounting <Viewer>.');
|
|
@@ -313,12 +322,18 @@ const Viewer = forwardRef(function Viewer({ children, hoverStyles = DEFAULT_HOVE
|
|
|
313
322
|
const isDark = useViewer((state) => state.sceneAppearance.appearance === 'dark');
|
|
314
323
|
const sceneTheme = useViewer((state) => state.sceneTheme);
|
|
315
324
|
const previousLampTheme = useRef(sceneTheme);
|
|
325
|
+
// Scene themes drive the lamps only in monochrome; the colored appearance's daylight follows
|
|
326
|
+
// its time of day instead.
|
|
327
|
+
const colored = useViewer((state) => state.textures);
|
|
328
|
+
const themeDrivesLamps = !(daylight && colored);
|
|
316
329
|
useEffect(() => {
|
|
330
|
+
if (!themeDrivesLamps)
|
|
331
|
+
return;
|
|
317
332
|
useInteractive
|
|
318
333
|
.getState()
|
|
319
334
|
.setLampDefault(getSceneTheme(sceneTheme).appearance === 'dark', previousLampTheme.current !== sceneTheme);
|
|
320
335
|
previousLampTheme.current = sceneTheme;
|
|
321
|
-
}, [sceneTheme]);
|
|
336
|
+
}, [sceneTheme, themeDrivesLamps]);
|
|
322
337
|
const transparentBackground = useViewer((state) => state.transparentBackground);
|
|
323
338
|
// The shadows toggle drives `renderer.shadowMap.enabled` (via the Canvas
|
|
324
339
|
// `shadows` prop) rather than the lights' `castShadow`: toggling castShadow
|
|
@@ -361,7 +376,7 @@ const Viewer = forwardRef(function Viewer({ children, hoverStyles = DEFAULT_HOVE
|
|
|
361
376
|
if (!hasDefaultRender || typeof window === 'undefined')
|
|
362
377
|
return;
|
|
363
378
|
let persistedState = {};
|
|
364
|
-
const rawPreferences =
|
|
379
|
+
const rawPreferences = viewerPreferenceStorage.getItem('viewer-preferences');
|
|
365
380
|
if (rawPreferences) {
|
|
366
381
|
try {
|
|
367
382
|
const parsed = JSON.parse(rawPreferences);
|
|
@@ -384,7 +399,10 @@ const Viewer = forwardRef(function Viewer({ children, hoverStyles = DEFAULT_HOVE
|
|
|
384
399
|
// Coarse-pointer devices (phones/tablets) get a tighter DPR ceiling to keep
|
|
385
400
|
// fragment-shader cost down — saves another ~30% over 1.5x on high-DPI mobile.
|
|
386
401
|
// Desktops (fine pointer) keep the original 1.5 cap.
|
|
387
|
-
|
|
402
|
+
// Pixel-ratio cap per quality tier (touch devices default to Medium).
|
|
403
|
+
const renderQuality = useViewer((state) => state.renderQuality);
|
|
404
|
+
const softwareRenderer = useViewer((state) => state.softwareRenderer);
|
|
405
|
+
const maxDpr = MAX_DPR[renderQuality];
|
|
388
406
|
const showGpuFallback = rendererInitFailed;
|
|
389
407
|
// When we can't mount the GPU canvas, the SceneReadyTracker never mounts and
|
|
390
408
|
// the host editor would otherwise wait on its scene-readiness timeout. Signal
|
|
@@ -404,7 +422,10 @@ const Viewer = forwardRef(function Viewer({ children, hoverStyles = DEFAULT_HOVE
|
|
|
404
422
|
if (showGpuFallback) {
|
|
405
423
|
return _jsx(UnsupportedGpuViewerFallback, {});
|
|
406
424
|
}
|
|
407
|
-
return (_jsxs(_Fragment, { children: [(perf || PERF_OVERLAY_ENABLED) && _jsx(PerfPanel, {}), _jsx(Canvas, { ref: subscribeWallBuildInteractions, camera: { position: [50, 50, 50], fov: 50 },
|
|
425
|
+
return (_jsxs(_Fragment, { children: [(perf || PERF_OVERLAY_ENABLED) && _jsx(PerfPanel, {}), _jsx(Canvas, { ref: subscribeWallBuildInteractions, camera: { position: [50, 50, 50], fov: 50 }, style: {
|
|
426
|
+
backgroundColor: transparentBackground ? 'transparent' : isDark ? '#1f2433' : '#fafafa',
|
|
427
|
+
transition: 'background-color 700ms cubic-bezier(0.4, 0, 0.2, 1), color 700ms cubic-bezier(0.4, 0, 0.2, 1)',
|
|
428
|
+
}, dpr: [1, maxDpr], events: pointerEvents, frameloop: "never", gl: ((props) => {
|
|
408
429
|
const canvas = props.canvas;
|
|
409
430
|
const cached = canvas ? WEBGPU_RENDERER_CACHE.get(canvas) : undefined;
|
|
410
431
|
if (cached)
|
|
@@ -434,6 +455,11 @@ const Viewer = forwardRef(function Viewer({ children, hoverStyles = DEFAULT_HOVE
|
|
|
434
455
|
});
|
|
435
456
|
if (result.status === 'ready') {
|
|
436
457
|
installEmptyDrawGuard(result.renderer);
|
|
458
|
+
// Before the first frame, so the post-processing builds once at the device's tier.
|
|
459
|
+
const signals = readDeviceSignals(result.backend, result.renderer.backend?.device);
|
|
460
|
+
useViewer
|
|
461
|
+
.getState()
|
|
462
|
+
.setDetectedRenderQuality(classifyDevice(signals), isSoftwareRenderer(signals));
|
|
437
463
|
return result.renderer;
|
|
438
464
|
}
|
|
439
465
|
if (canvas)
|
|
@@ -456,11 +482,13 @@ const Viewer = forwardRef(function Viewer({ children, hoverStyles = DEFAULT_HOVE
|
|
|
456
482
|
}, shadows: {
|
|
457
483
|
type: THREE.PCFShadowMap,
|
|
458
484
|
enabled: shadowsEnabled,
|
|
459
|
-
}, children: _jsx(ImmersiveXRPresentationProvider, { enabled: immersiveActive, children: ImmersiveSession ? (_jsx(ImmersiveSession, { children: _jsx(ViewerScene, { disablePostFx: true, hoverStyles: hoverStyles, immersiveXR: true, renderErrorHandlers: [immersive.onError, onRenderError], onSceneReadyChange: onSceneReadyChange, perf: perf, sceneReadyKey: sceneReadyKey, sceneReadyMaxWaitMs: sceneReadyMaxWaitMs, selectionManager: selectionManager, useBvh: useBvh, SceneWrapper: immersive.Scene, children: children }) })) : (_jsxs(_Fragment, { children: [_jsx(FrameLimiter, { fps: maxFps, onFrameError: composeRenderErrorHandlers(onRenderError), paused: renderPaused }), _jsx(ViewerScene, { disablePostFx: disablePostFx, hoverStyles: hoverStyles, renderErrorHandlers: [onRenderError], onSceneReadyChange: onSceneReadyChange, perf: perf, sceneReadyKey: sceneReadyKey, sceneReadyMaxWaitMs: sceneReadyMaxWaitMs, selectionManager: selectionManager, useBvh: useBvh, children: children })] })) }) }, immersiveActive ? 'webgl-xr' : 'webgpu')] }));
|
|
485
|
+
}, children: _jsx(ImmersiveXRPresentationProvider, { enabled: immersiveActive, children: ImmersiveSession ? (_jsx(ImmersiveSession, { children: _jsx(ViewerScene, { daylight: daylight, disablePostFx: true, hoverStyles: hoverStyles, immersiveXR: true, renderErrorHandlers: [immersive.onError, onRenderError], onSceneReadyChange: onSceneReadyChange, perf: perf, sceneReadyKey: sceneReadyKey, sceneReadyMaxWaitMs: sceneReadyMaxWaitMs, selectionManager: selectionManager, useBvh: useBvh, SceneWrapper: immersive.Scene, children: children }) })) : (_jsxs(_Fragment, { children: [_jsx(FrameLimiter, { fps: softwareRenderer ? Math.min(maxFps, SOFTWARE_MAX_FPS) : maxFps, onFrameError: composeRenderErrorHandlers(onRenderError), paused: renderPaused }), _jsx(ViewerScene, { daylight: daylight, disablePostFx: disablePostFx, hoverStyles: hoverStyles, renderErrorHandlers: [onRenderError], onSceneReadyChange: onSceneReadyChange, perf: perf, sceneReadyKey: sceneReadyKey, sceneReadyMaxWaitMs: sceneReadyMaxWaitMs, selectionManager: selectionManager, useBvh: useBvh, children: children })] })) }) }, immersiveActive ? 'webgl-xr' : 'webgpu')] }));
|
|
460
486
|
});
|
|
461
|
-
function ViewerScene({ children, disablePostFx, hoverStyles, immersiveXR = false, renderErrorHandlers, onSceneReadyChange, perf, sceneReadyKey, sceneReadyMaxWaitMs, selectionManager, useBvh, SceneWrapper, }) {
|
|
487
|
+
function ViewerScene({ children, daylight, disablePostFx, hoverStyles, immersiveXR = false, renderErrorHandlers, onSceneReadyChange, perf, sceneReadyKey, sceneReadyMaxWaitMs, selectionManager, useBvh, SceneWrapper, }) {
|
|
488
|
+
// Colored appearance = the built-in daylight look; monochrome keeps the scene themes.
|
|
489
|
+
const colored = useViewer((state) => state.textures);
|
|
462
490
|
const renderedScene = useBvh ? (_jsx(SceneBvh, { children: _jsx(SceneRenderer, {}) })) : (_jsx(SceneRenderer, {}));
|
|
463
491
|
const spatialScene = (_jsxs(_Fragment, { children: [renderedScene, _jsx(FloorElevationSystem, {}), _jsx(GeometrySystem, {}), _jsx(StairOpeningSystem, {}), _jsx(RoofElevationSystem, {}), _jsx(RegisteredSystems, {}), BATCH_SPIKE_ENABLED && _jsx(BatchedMeshSpike, {}), children] }));
|
|
464
|
-
return (_jsxs(_Fragment, { children: [_jsx(ViewerCamera, { immersiveXR: immersiveXR }), immersiveXR && _jsx(ImmersiveXRBackground, {}), _jsx(PointerRaycastLayers, {}), _jsx(GPUDeviceWatcher, { intentionalWebGL: immersiveXR }), _jsx(ToneMappingExposure, {}), _jsx(SceneReadyTracker, { onSceneReadyChange: onSceneReadyChange, sceneReadyKey: sceneReadyKey, sceneReadyMaxWaitMs: sceneReadyMaxWaitMs }), _jsxs(SceneErrorBoundary, { handlers: renderErrorHandlers, children: [_jsx(Lights, {}), SceneWrapper ? _jsx(SceneWrapper, { children: spatialScene }) : spatialScene, !immersiveXR && _jsx(PostProcessing, { disablePostFx: disablePostFx, hoverStyles: hoverStyles }), selectionManager === 'default' && _jsx(SelectionManager, {}), (perf || PERF_OVERLAY_ENABLED) && _jsx(PerfMonitor, {}), (perf || PERF_OVERLAY_ENABLED) && _jsx(PerfActionSettleSystem, {})] })] }));
|
|
492
|
+
return (_jsxs(_Fragment, { children: [_jsx(ViewerCamera, { immersiveXR: immersiveXR }), immersiveXR && _jsx(ImmersiveXRBackground, {}), _jsx(PointerRaycastLayers, {}), _jsx(GPUDeviceWatcher, { intentionalWebGL: immersiveXR }), _jsx(ToneMappingExposure, {}), _jsx(SceneReadyTracker, { onSceneReadyChange: onSceneReadyChange, sceneReadyKey: sceneReadyKey, sceneReadyMaxWaitMs: sceneReadyMaxWaitMs }), _jsxs(SceneErrorBoundary, { handlers: renderErrorHandlers, children: [daylight && colored && _jsx(DaylightAtmosphere, {}), _jsx(Lights, {}), SceneWrapper ? _jsx(SceneWrapper, { children: spatialScene }) : spatialScene, !immersiveXR && _jsx(PostProcessing, { disablePostFx: disablePostFx, hoverStyles: hoverStyles }), selectionManager === 'default' && _jsx(SelectionManager, {}), (perf || PERF_OVERLAY_ENABLED) && _jsx(PerfMonitor, {}), (perf || PERF_OVERLAY_ENABLED) && _jsx(PerfActionSettleSystem, {})] })] }));
|
|
465
493
|
}
|
|
466
494
|
export default Viewer;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lights.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/lights.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"lights.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/lights.tsx"],"names":[],"mappings":"AAoGA,wBAAgB,MAAM,gCA+XrB"}
|
|
@@ -3,6 +3,7 @@ import { sceneRegistry } from '@pascal-app/core';
|
|
|
3
3
|
import { useFrame } from '@react-three/fiber';
|
|
4
4
|
import { useEffect, useMemo, useRef } from 'react';
|
|
5
5
|
import * as THREE from 'three/webgpu';
|
|
6
|
+
import { isDaylightAtmosphere } from '../../lib/daylight/look';
|
|
6
7
|
import { getKeyLightDirectionOverride } from '../../lib/key-light-override';
|
|
7
8
|
import { SHADOW_ONLY_LAYER } from '../../lib/layers';
|
|
8
9
|
import { registerSkyLights } from '../../lib/underground-ambiance';
|
|
@@ -97,7 +98,10 @@ export function Lights() {
|
|
|
97
98
|
fillMounted.current = true;
|
|
98
99
|
const filled = fillMounted.current;
|
|
99
100
|
const atmosphere = useSceneAtmosphere();
|
|
100
|
-
|
|
101
|
+
// The daylight look lights with one key (sun, or moon at night) and the sky environment only:
|
|
102
|
+
// no moon slot, hemisphere or ambient light (each light costs every material's shader).
|
|
103
|
+
const daylight = isDaylightAtmosphere(atmosphere);
|
|
104
|
+
const lightSlots = useMemo(() => Array.from({ length: daylight ? 1 : atmosphere ? 2 : theme.lights.length }, (_, index) => index), [atmosphere, daylight, theme.lights.length]);
|
|
101
105
|
const lightRefs = useRef([]);
|
|
102
106
|
const shadowCamera = useRef(null);
|
|
103
107
|
// Initial ortho half-size; overridden each refresh to fit the building.
|
|
@@ -109,6 +113,10 @@ export function Lights() {
|
|
|
109
113
|
const boundsBox = useRef(new THREE.Box3()); // scratch: union AABB
|
|
110
114
|
const boundsSphere = useRef(new THREE.Sphere()); // scratch: fitted sphere
|
|
111
115
|
const lastBoundsTime = useRef(-1); // last refresh timestamp (-1 = never)
|
|
116
|
+
// Daylight sun shadows render on change only (see `shadowSignature`).
|
|
117
|
+
const shadowCasters = useRef([]);
|
|
118
|
+
const shadowCasterTime = useRef(-1);
|
|
119
|
+
const lastShadowSignature = useRef(Number.NaN);
|
|
112
120
|
const shadowHelpers = useRef([]);
|
|
113
121
|
const hemiRef = useRef(null);
|
|
114
122
|
const ambientRef = useRef(null);
|
|
@@ -135,6 +143,9 @@ export function Lights() {
|
|
|
135
143
|
const dt = Math.min(delta, 0.1) * 4;
|
|
136
144
|
const sourceShadow = atmosphere ? undefined : theme.lights[0]?.shadow;
|
|
137
145
|
const sourceCamera = sourceShadow?.camera;
|
|
146
|
+
// The daylight look: one key (sun or moon) with its own shadow settings, frustum fitted
|
|
147
|
+
// tightly around the building in light space.
|
|
148
|
+
const daylightShadow = isDaylightAtmosphere(atmosphere) ? atmosphere.shadow : undefined;
|
|
138
149
|
// Atmosphere directions are stable mutable vectors. Keep both light
|
|
139
150
|
// resources mounted and move them imperatively so time/angle changes never
|
|
140
151
|
// rebuild materials or shadow resources.
|
|
@@ -168,11 +179,24 @@ export function Lights() {
|
|
|
168
179
|
if (!sourceCamera && now - lastBoundsTime.current >= BOUNDS_REFRESH_INTERVAL) {
|
|
169
180
|
lastBoundsTime.current = now;
|
|
170
181
|
const box = boundsBox.current.makeEmpty();
|
|
182
|
+
const counted = new Set();
|
|
171
183
|
for (const [id, obj] of sceneRegistry.nodes) {
|
|
172
184
|
if (SHADOW_EXCLUDED_TYPES.some((type) => sceneRegistry.byType[type].has(id)))
|
|
173
185
|
continue;
|
|
174
|
-
|
|
186
|
+
counted.add(obj);
|
|
175
187
|
}
|
|
188
|
+
// A counted node's subtree already holds the nodes below it: walk each mesh once, not
|
|
189
|
+
// once per counted ancestor (building, level, wall, item…).
|
|
190
|
+
for (const obj of counted) {
|
|
191
|
+
let nested = false;
|
|
192
|
+
for (let p = obj.parent; p && !nested; p = p.parent)
|
|
193
|
+
nested = counted.has(p);
|
|
194
|
+
if (!nested)
|
|
195
|
+
expandByWorldBounds(box, obj);
|
|
196
|
+
}
|
|
197
|
+
// Baked artifacts stay out of the registry: frame their shadow casters instead.
|
|
198
|
+
if (box.isEmpty() && daylightShadow)
|
|
199
|
+
expandByShadowCasters(state.scene, box);
|
|
176
200
|
box.getBoundingSphere(boundsSphere.current);
|
|
177
201
|
const center = boundsSphere.current.center;
|
|
178
202
|
const radius = boundsSphere.current.radius;
|
|
@@ -235,7 +259,29 @@ export function Lights() {
|
|
|
235
259
|
light.position.set(tx, ty, tz).add(shadowDir.current);
|
|
236
260
|
}
|
|
237
261
|
const cam = light.shadow?.camera;
|
|
238
|
-
if (cam) {
|
|
262
|
+
if (cam && daylightShadow && !sourceCamera) {
|
|
263
|
+
light.shadow.mapSize.setScalar(daylightShadow.mapSize);
|
|
264
|
+
light.shadow.bias = daylightShadow.bias;
|
|
265
|
+
light.shadow.normalBias = daylightShadow.normalBias;
|
|
266
|
+
light.shadow.radius = daylightShadow.radius;
|
|
267
|
+
cam.layers.enable(SHADOW_ONLY_LAYER);
|
|
268
|
+
fitShadowToBounds(light, boundsBox.current, shadowDir.current);
|
|
269
|
+
// The sun's map re-renders only when something it depends on changes: its placement,
|
|
270
|
+
// frustum or settings, or a caster that moved, appeared, vanished or changed shape.
|
|
271
|
+
// A walkthrough or an orbit moves only the eye, so the map is reused frame to frame.
|
|
272
|
+
light.shadow.autoUpdate = false;
|
|
273
|
+
if (now - shadowCasterTime.current >= BOUNDS_REFRESH_INTERVAL) {
|
|
274
|
+
shadowCasterTime.current = now;
|
|
275
|
+
collectShadowCasters(state.scene, shadowCasters.current);
|
|
276
|
+
}
|
|
277
|
+
const signature = shadowSignature(light, cam, shadowCasters.current);
|
|
278
|
+
if (signature !== lastShadowSignature.current) {
|
|
279
|
+
lastShadowSignature.current = signature;
|
|
280
|
+
light.shadow.needsUpdate = true;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
else if (cam) {
|
|
284
|
+
light.shadow.autoUpdate = true;
|
|
239
285
|
// The calibrated building bias detaches furniture-scale contact shadows.
|
|
240
286
|
// Small scenes need finer shadow edges too; retain the building map budget.
|
|
241
287
|
const smallScene = !sourceCamera && shadowRadius.current < 1.5;
|
|
@@ -300,7 +346,9 @@ export function Lights() {
|
|
|
300
346
|
light.color.lerp(target, dt);
|
|
301
347
|
}
|
|
302
348
|
if (index === 0 && light.shadow?.intensity !== undefined) {
|
|
303
|
-
const shadowIntensity =
|
|
349
|
+
const shadowIntensity = daylightShadow
|
|
350
|
+
? 1
|
|
351
|
+
: (sourceShadow?.intensity ?? (intensity <= 1 ? intensity : MAX_SHADOW_INTENSITY));
|
|
304
352
|
light.shadow.intensity =
|
|
305
353
|
atmosphere || !initialized.current
|
|
306
354
|
? shadowIntensity
|
|
@@ -353,5 +401,144 @@ export function Lights() {
|
|
|
353
401
|
: (themeLight?.position ?? [0, 1, 0]), ref: (light) => {
|
|
354
402
|
lightRefs.current[index] = light;
|
|
355
403
|
}, "shadow-bias": SHADOW_DEPTH_BIAS, "shadow-mapSize": [1024, 1024], "shadow-normalBias": SHADOW_NORMAL_BIAS, "shadow-radius": 2, children: index === 0 && keyCastsShadow ? (_jsx("orthographicCamera", { attach: "shadow-camera", bottom: -shadowCameraSize, far: 400, left: -shadowCameraSize, near: 1, ref: shadowCamera, right: shadowCameraSize, top: shadowCameraSize })) : null }, index));
|
|
356
|
-
}), atmosphere || theme.hemi ? _jsx("hemisphereLight", { ref: hemiRef }) : null, _jsx("ambientLight", { ref: ambientRef }), filled && _jsx(UndergroundFill, { lightRef: fillRef }), _jsx(UndergroundAmbiance, {})] }));
|
|
404
|
+
}), !daylight && (atmosphere || theme.hemi) ? _jsx("hemisphereLight", { ref: hemiRef }) : null, daylight ? null : _jsx("ambientLight", { ref: ambientRef }), filled && _jsx(UndergroundFill, { lightRef: fillRef }), _jsx(UndergroundAmbiance, {})] }));
|
|
405
|
+
}
|
|
406
|
+
const _fitCenter = new THREE.Vector3();
|
|
407
|
+
const _fitSize = new THREE.Vector3();
|
|
408
|
+
const _fitCorner = new THREE.Vector3();
|
|
409
|
+
const _fitBox = new THREE.Box3();
|
|
410
|
+
const _fitBounds = new THREE.Box3();
|
|
411
|
+
const _fitView = new THREE.Matrix4();
|
|
412
|
+
const _fitUp = new THREE.Vector3(0, 1, 0);
|
|
413
|
+
const _fitMargin = new THREE.Vector3(2, 0, 2);
|
|
414
|
+
/**
|
|
415
|
+
* Daylight key: place the light on its direction around the building and fit the ortho shadow
|
|
416
|
+
* camera to the building box (+2 m of ground) projected into light space — the tightest frustum,
|
|
417
|
+
* so the map's texels all land on the building. Its depth reaches on to where the casters' shadows
|
|
418
|
+
* meet the ground: a receiver beyond the far plane reads as fully lit, which cut long shadows off
|
|
419
|
+
* square. A shadow keeps its caster's light-space x/y, so only the depth grows, not the texels.
|
|
420
|
+
*/
|
|
421
|
+
function fitShadowToBounds(light, bounds, direction) {
|
|
422
|
+
if (bounds.isEmpty())
|
|
423
|
+
return;
|
|
424
|
+
const center = bounds.getCenter(_fitCenter);
|
|
425
|
+
const radius = bounds.getSize(_fitSize).length() / 2 + 2;
|
|
426
|
+
const toLight = _fitSize.copy(direction).normalize();
|
|
427
|
+
light.position.copy(center).addScaledVector(toLight, radius * 2);
|
|
428
|
+
light.target.position.copy(center);
|
|
429
|
+
light.target.updateMatrixWorld();
|
|
430
|
+
light.updateMatrixWorld();
|
|
431
|
+
_fitView.lookAt(light.position, center, _fitUp).invert();
|
|
432
|
+
_fitView.setPosition(0, 0, 0);
|
|
433
|
+
const b = _fitBounds.copy(bounds).expandByVector(_fitMargin);
|
|
434
|
+
const box = _fitBox.makeEmpty();
|
|
435
|
+
// The ground under the casters (the site ground sits just below 0).
|
|
436
|
+
const groundY = Math.min(bounds.min.y, -0.07);
|
|
437
|
+
for (let i = 0; i < 8; i++) {
|
|
438
|
+
const y = i & 2 ? b.max.y : b.min.y;
|
|
439
|
+
_fitCorner
|
|
440
|
+
.set(i & 1 ? b.max.x : b.min.x, y, i & 4 ? b.max.z : b.min.z)
|
|
441
|
+
.sub(light.position)
|
|
442
|
+
.applyMatrix4(_fitView);
|
|
443
|
+
box.expandByPoint(_fitCorner);
|
|
444
|
+
// Down the light ray to the ground: same x/y in light space, only deeper.
|
|
445
|
+
if (toLight.y > 0)
|
|
446
|
+
box.min.z = Math.min(box.min.z, _fitCorner.z - (y - groundY) / toLight.y);
|
|
447
|
+
}
|
|
448
|
+
const cam = light.shadow.camera;
|
|
449
|
+
cam.left = box.min.x - 0.5;
|
|
450
|
+
cam.right = box.max.x + 0.5;
|
|
451
|
+
cam.bottom = box.min.y - 0.5;
|
|
452
|
+
cam.top = box.max.y + 0.5;
|
|
453
|
+
cam.near = Math.max(0.1, -box.max.z - 1);
|
|
454
|
+
cam.far = -box.min.z + 1;
|
|
455
|
+
cam.updateProjectionMatrix();
|
|
456
|
+
}
|
|
457
|
+
/** Every shadow-casting mesh under `root` (visible or not: a level shown again must count). */
|
|
458
|
+
function collectShadowCasters(root, out) {
|
|
459
|
+
out.length = 0;
|
|
460
|
+
root.traverse((object) => {
|
|
461
|
+
const mesh = object;
|
|
462
|
+
if (mesh.isMesh && mesh.castShadow)
|
|
463
|
+
out.push(mesh);
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* A number that changes whenever the sun's shadow map would: the light's world placement and
|
|
468
|
+
* projection, its map settings, and each caster's world transform, shown state (through its
|
|
469
|
+
* ancestors) and geometry. Cheap enough per frame: a few reads per caster, no allocation.
|
|
470
|
+
*/
|
|
471
|
+
function shadowSignature(light, cam, casters) {
|
|
472
|
+
// A weighted sum, not a multiply-and-wrap hash: it stays exact to ~1e-7 at these magnitudes,
|
|
473
|
+
// so a door turning by a fraction of a degree still changes it.
|
|
474
|
+
let h = light.shadow.mapSize.x + light.shadow.bias * 1e4 + light.shadow.normalBias * 1e3;
|
|
475
|
+
h += light.shadow.radius * 7;
|
|
476
|
+
const lm = light.matrixWorld.elements;
|
|
477
|
+
const pm = cam.projectionMatrix.elements;
|
|
478
|
+
for (let i = 0; i < 16; i++)
|
|
479
|
+
h += (i + 1) * (lm[i] * 1.013 + pm[i] * 0.917);
|
|
480
|
+
for (let i = 0; i < casters.length; i++) {
|
|
481
|
+
const mesh = casters[i];
|
|
482
|
+
let shown = mesh.castShadow;
|
|
483
|
+
for (let x = mesh; x && shown; x = x.parent)
|
|
484
|
+
shown = x.visible;
|
|
485
|
+
if (!shown)
|
|
486
|
+
continue;
|
|
487
|
+
const m = mesh.matrixWorld.elements;
|
|
488
|
+
const w = (i % 89) + 1;
|
|
489
|
+
h +=
|
|
490
|
+
w *
|
|
491
|
+
(m[0] +
|
|
492
|
+
m[1] * 2 +
|
|
493
|
+
m[2] * 3 +
|
|
494
|
+
m[5] * 5 +
|
|
495
|
+
m[10] * 7 +
|
|
496
|
+
m[12] * 11 +
|
|
497
|
+
m[13] * 13 +
|
|
498
|
+
m[14] * 17 +
|
|
499
|
+
mesh.geometry.id * 0.019 +
|
|
500
|
+
0.5);
|
|
501
|
+
}
|
|
502
|
+
return h;
|
|
503
|
+
}
|
|
504
|
+
const _objectBox = new THREE.Box3();
|
|
505
|
+
/**
|
|
506
|
+
* `Box3.expandByObject` without its `updateWorldMatrix` on every object visited, which recomposed
|
|
507
|
+
* ~10k matrices per refresh: the frame's render already brought world matrices up to date
|
|
508
|
+
* (scene-matrices.ts), and bounds a frame old are fine for fitting the shadow frustum.
|
|
509
|
+
*/
|
|
510
|
+
function expandByWorldBounds(box, root) {
|
|
511
|
+
root.traverse((object) => {
|
|
512
|
+
const geometry = object.geometry;
|
|
513
|
+
if (geometry === undefined)
|
|
514
|
+
return;
|
|
515
|
+
const owned = object;
|
|
516
|
+
if (owned.boundingBox !== undefined) {
|
|
517
|
+
if (owned.boundingBox === null)
|
|
518
|
+
owned.computeBoundingBox?.();
|
|
519
|
+
if (!owned.boundingBox)
|
|
520
|
+
return;
|
|
521
|
+
_objectBox.copy(owned.boundingBox);
|
|
522
|
+
}
|
|
523
|
+
else {
|
|
524
|
+
if (geometry.boundingBox === null)
|
|
525
|
+
geometry.computeBoundingBox();
|
|
526
|
+
_objectBox.copy(geometry.boundingBox);
|
|
527
|
+
}
|
|
528
|
+
_objectBox.applyMatrix4(object.matrixWorld);
|
|
529
|
+
box.union(_objectBox);
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
/** Shadow-casting meshes of a scene (skipping the effectively infinite grounds). */
|
|
533
|
+
function expandByShadowCasters(scene, box) {
|
|
534
|
+
const mesh = new THREE.Box3();
|
|
535
|
+
scene.traverseVisible((object) => {
|
|
536
|
+
const candidate = object;
|
|
537
|
+
if (!candidate.isMesh || !candidate.castShadow)
|
|
538
|
+
return;
|
|
539
|
+
mesh.setFromObject(candidate);
|
|
540
|
+
if (mesh.isEmpty() || mesh.max.x - mesh.min.x > 500 || mesh.max.z - mesh.min.z > 500)
|
|
541
|
+
return;
|
|
542
|
+
box.union(mesh);
|
|
543
|
+
});
|
|
357
544
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"post-processing.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/post-processing.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"post-processing.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/post-processing.tsx"],"names":[],"mappings":"AA+FA,eAAO,MAAM,YAAY;IACvB,QAAQ;IACR,UAAU;CACX,CAAA;AAGD,eAAO,MAAM,WAAW;IACtB,OAAO;IACP,UAAU;IACV,SAAS;IACT,MAAM;IACN,SAAS;IACT,SAAS;IACT,gBAAgB;IAChB,WAAW;IACX,WAAW;IACX,kBAAkB;IAClB,sBAAsB;IACtB,oBAAoB;CACrB,CAAA;AAgED,MAAM,MAAM,UAAU,GAAG;IACvB,YAAY,EAAE,MAAM,CAAA;IACpB,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,OAAO,CAAA;CACf,CAAA;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,OAAO,EAAE,UAAU,CAAA;CACpB,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;AAS9B,eAAO,MAAM,oBAAoB,EAAE,WAElC,CAAA;AA+CD,QAAA,MAAM,oBAAoB,oCAGvB;IACD,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB,6EAA6E;IAC7E,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB,SAkvBA,CAAA;eAEc,oBAAoB"}
|