@pascal-app/viewer 0.7.0 → 0.9.1
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/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 +38 -22
- package/dist/components/renderers/parametric-node-renderer.d.ts +5 -0
- package/dist/components/renderers/parametric-node-renderer.d.ts.map +1 -0
- package/dist/components/renderers/parametric-node-renderer.js +34 -0
- package/dist/components/renderers/scene-renderer.d.ts +1 -1
- package/dist/components/renderers/scene-renderer.d.ts.map +1 -1
- package/dist/components/viewer/frame-limiter.d.ts.map +1 -1
- package/dist/components/viewer/frame-limiter.js +5 -1
- package/dist/components/viewer/ground-occluder.d.ts +1 -1
- package/dist/components/viewer/ground-occluder.d.ts.map +1 -1
- package/dist/components/viewer/ground-occluder.js +12 -24
- package/dist/components/viewer/index.d.ts +38 -1
- package/dist/components/viewer/index.d.ts.map +1 -1
- package/dist/components/viewer/index.js +183 -60
- package/dist/components/viewer/lights.d.ts +1 -1
- package/dist/components/viewer/lights.d.ts.map +1 -1
- package/dist/components/viewer/lights.js +170 -43
- package/dist/components/viewer/perf-monitor.d.ts +1 -1
- package/dist/components/viewer/perf-monitor.d.ts.map +1 -1
- package/dist/components/viewer/perf-monitor.js +99 -11
- package/dist/components/viewer/post-processing.d.ts.map +1 -1
- package/dist/components/viewer/post-processing.js +217 -61
- package/dist/components/viewer/registered-systems.d.ts +12 -0
- package/dist/components/viewer/registered-systems.d.ts.map +1 -0
- package/dist/components/viewer/registered-systems.js +47 -0
- package/dist/components/viewer/scene-bvh.d.ts +17 -0
- package/dist/components/viewer/scene-bvh.d.ts.map +1 -0
- package/dist/components/viewer/scene-bvh.js +94 -0
- package/dist/components/viewer/selection-manager.d.ts +1 -1
- package/dist/components/viewer/selection-manager.d.ts.map +1 -1
- package/dist/components/viewer/selection-manager.js +20 -5
- package/dist/components/viewer/viewer-camera.d.ts +1 -1
- package/dist/components/viewer/viewer-camera.d.ts.map +1 -1
- package/dist/components/viewer/walkthrough-controls.d.ts +1 -1
- package/dist/components/viewer/walkthrough-controls.d.ts.map +1 -1
- package/dist/hooks/use-gltf-ktx2.d.ts.map +1 -1
- package/dist/hooks/use-node-events.d.ts +5 -73
- package/dist/hooks/use-node-events.d.ts.map +1 -1
- package/dist/hooks/use-node-events.js +19 -7
- package/dist/index.d.ts +36 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +67 -3
- package/dist/lib/csg-utils.d.ts +16 -0
- package/dist/lib/csg-utils.d.ts.map +1 -0
- package/dist/lib/csg-utils.js +33 -0
- package/dist/lib/drawable-geometry.d.ts +21 -0
- package/dist/lib/drawable-geometry.d.ts.map +1 -0
- package/dist/lib/drawable-geometry.js +22 -0
- package/dist/lib/edge-style.d.ts +3 -0
- package/dist/lib/edge-style.d.ts.map +1 -0
- package/dist/lib/edge-style.js +11 -0
- package/dist/lib/gpu-perf.d.ts +4 -0
- package/dist/lib/gpu-perf.d.ts.map +1 -0
- package/dist/lib/gpu-perf.js +23 -0
- package/dist/lib/ink-edges.d.ts +9 -0
- package/dist/lib/ink-edges.d.ts.map +1 -0
- package/dist/lib/ink-edges.js +43 -0
- package/dist/lib/isolation.d.ts +39 -0
- package/dist/lib/isolation.d.ts.map +1 -0
- package/dist/lib/isolation.js +103 -0
- package/dist/lib/layers.d.ts +18 -0
- package/dist/lib/layers.d.ts.map +1 -1
- package/dist/lib/layers.js +18 -0
- package/dist/lib/materials.d.ts +25 -15
- package/dist/lib/materials.d.ts.map +1 -1
- package/dist/lib/materials.js +277 -63
- package/dist/lib/merged-outline-node.d.ts.map +1 -1
- package/dist/lib/merged-outline-node.js +11 -11
- package/dist/lib/polygon-union.d.ts +4 -0
- package/dist/lib/polygon-union.d.ts.map +1 -0
- package/dist/lib/polygon-union.js +289 -0
- package/dist/lib/polygon-union.test.d.ts +2 -0
- package/dist/lib/polygon-union.test.d.ts.map +1 -0
- package/dist/lib/polygon-union.test.js +105 -0
- package/dist/lib/scene-themes.d.ts +29 -0
- package/dist/lib/scene-themes.d.ts.map +1 -0
- package/dist/lib/scene-themes.js +193 -0
- package/dist/store/use-viewer.d.ts +42 -4
- package/dist/store/use-viewer.d.ts.map +1 -1
- package/dist/store/use-viewer.js +26 -3
- package/dist/systems/ceiling/ceiling-system.d.ts +6 -2
- package/dist/systems/ceiling/ceiling-system.d.ts.map +1 -1
- package/dist/systems/ceiling/ceiling-system.js +62 -12
- package/dist/systems/door/door-system.d.ts.map +1 -1
- package/dist/systems/door/door-system.js +373 -22
- package/dist/systems/elevator/elevator-interaction-system.d.ts +2 -0
- package/dist/systems/elevator/elevator-interaction-system.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-interaction-system.js +62 -0
- package/dist/systems/fence/fence-system.d.ts +3 -0
- package/dist/systems/fence/fence-system.d.ts.map +1 -1
- package/dist/systems/fence/fence-system.js +10 -6
- package/dist/systems/floor-elevation/floor-elevation-system.d.ts +20 -0
- package/dist/systems/floor-elevation/floor-elevation-system.d.ts.map +1 -0
- package/dist/systems/floor-elevation/floor-elevation-system.js +78 -0
- package/dist/systems/geometry/geometry-system.d.ts +34 -0
- package/dist/systems/geometry/geometry-system.d.ts.map +1 -0
- package/dist/systems/geometry/geometry-system.js +279 -0
- package/dist/systems/guide/guide-system.d.ts.map +1 -1
- package/dist/systems/guide/guide-system.js +1 -26
- package/dist/systems/interactive/interactive-system.d.ts +1 -1
- package/dist/systems/interactive/interactive-system.d.ts.map +1 -1
- package/dist/systems/item/item-system.d.ts +7 -0
- package/dist/systems/item/item-system.d.ts.map +1 -1
- package/dist/systems/item/item-system.js +8 -11
- 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 +18 -5
- package/dist/systems/roof/roof-materials.d.ts +3 -2
- package/dist/systems/roof/roof-materials.d.ts.map +1 -1
- package/dist/systems/roof/roof-materials.js +38 -14
- package/dist/systems/roof/roof-system.d.ts +27 -0
- package/dist/systems/roof/roof-system.d.ts.map +1 -1
- package/dist/systems/roof/roof-system.js +411 -71
- package/dist/systems/scan/scan-system.d.ts.map +1 -1
- package/dist/systems/scan/scan-system.js +1 -26
- package/dist/systems/slab/slab-system.d.ts.map +1 -1
- package/dist/systems/slab/slab-system.js +167 -49
- package/dist/systems/slab/slab-system.test.d.ts +2 -0
- package/dist/systems/slab/slab-system.test.d.ts.map +1 -0
- package/dist/systems/slab/slab-system.test.js +62 -0
- package/dist/systems/stair/stair-materials.d.ts +4 -3
- package/dist/systems/stair/stair-materials.d.ts.map +1 -1
- package/dist/systems/stair/stair-materials.js +30 -15
- package/dist/systems/stair/stair-system.d.ts.map +1 -1
- package/dist/systems/stair/stair-system.js +58 -71
- package/dist/systems/surface-hole-geometry.d.ts +3 -0
- package/dist/systems/surface-hole-geometry.d.ts.map +1 -0
- package/dist/systems/surface-hole-geometry.js +4 -0
- package/dist/systems/wall/wall-cutout.d.ts.map +1 -1
- package/dist/systems/wall/wall-cutout.js +18 -2
- package/dist/systems/wall/wall-materials.d.ts +4 -3
- package/dist/systems/wall/wall-materials.d.ts.map +1 -1
- package/dist/systems/wall/wall-materials.js +52 -28
- package/dist/systems/wall/wall-system.d.ts +1 -1
- package/dist/systems/wall/wall-system.d.ts.map +1 -1
- package/dist/systems/wall/wall-system.js +179 -31
- package/dist/systems/window/window-system.d.ts.map +1 -1
- package/dist/systems/window/window-system.js +71 -7
- package/package.json +4 -5
- package/dist/components/renderers/building/building-renderer.d.ts +0 -5
- package/dist/components/renderers/building/building-renderer.d.ts.map +0 -1
- package/dist/components/renderers/building/building-renderer.js +0 -11
- package/dist/components/renderers/ceiling/ceiling-renderer.d.ts +0 -5
- package/dist/components/renderers/ceiling/ceiling-renderer.d.ts.map +0 -1
- package/dist/components/renderers/ceiling/ceiling-renderer.js +0 -58
- package/dist/components/renderers/column/column-renderer.d.ts +0 -5
- package/dist/components/renderers/column/column-renderer.d.ts.map +0 -1
- package/dist/components/renderers/column/column-renderer.js +0 -594
- package/dist/components/renderers/door/door-renderer.d.ts +0 -5
- package/dist/components/renderers/door/door-renderer.d.ts.map +0 -1
- package/dist/components/renderers/door/door-renderer.js +0 -16
- package/dist/components/renderers/fence/fence-renderer.d.ts +0 -5
- package/dist/components/renderers/fence/fence-renderer.d.ts.map +0 -1
- package/dist/components/renderers/fence/fence-renderer.js +0 -29
- package/dist/components/renderers/guide/guide-renderer.d.ts +0 -5
- package/dist/components/renderers/guide/guide-renderer.d.ts.map +0 -1
- package/dist/components/renderers/guide/guide-renderer.js +0 -38
- package/dist/components/renderers/item/item-renderer.d.ts +0 -5
- package/dist/components/renderers/item/item-renderer.d.ts.map +0 -1
- package/dist/components/renderers/item/item-renderer.js +0 -172
- package/dist/components/renderers/level/level-renderer.d.ts +0 -5
- package/dist/components/renderers/level/level-renderer.d.ts.map +0 -1
- package/dist/components/renderers/level/level-renderer.js +0 -11
- package/dist/components/renderers/roof/roof-materials.d.ts +0 -4
- package/dist/components/renderers/roof/roof-materials.d.ts.map +0 -1
- package/dist/components/renderers/roof/roof-materials.js +0 -16
- package/dist/components/renderers/roof/roof-renderer.d.ts +0 -5
- package/dist/components/renderers/roof/roof-renderer.d.ts.map +0 -1
- package/dist/components/renderers/roof/roof-renderer.js +0 -32
- package/dist/components/renderers/roof-segment/roof-segment-renderer.d.ts +0 -5
- package/dist/components/renderers/roof-segment/roof-segment-renderer.d.ts.map +0 -1
- package/dist/components/renderers/roof-segment/roof-segment-renderer.js +0 -40
- package/dist/components/renderers/scan/scan-renderer.d.ts +0 -5
- package/dist/components/renderers/scan/scan-renderer.d.ts.map +0 -1
- package/dist/components/renderers/scan/scan-renderer.js +0 -54
- package/dist/components/renderers/site/site-renderer.d.ts +0 -5
- package/dist/components/renderers/site/site-renderer.d.ts.map +0 -1
- package/dist/components/renderers/site/site-renderer.js +0 -106
- package/dist/components/renderers/slab/slab-renderer.d.ts +0 -5
- package/dist/components/renderers/slab/slab-renderer.d.ts.map +0 -1
- package/dist/components/renderers/slab/slab-renderer.js +0 -58
- package/dist/components/renderers/spawn/spawn-renderer.d.ts +0 -5
- package/dist/components/renderers/spawn/spawn-renderer.d.ts.map +0 -1
- package/dist/components/renderers/spawn/spawn-renderer.js +0 -32
- package/dist/components/renderers/stair/stair-renderer.d.ts +0 -5
- package/dist/components/renderers/stair/stair-renderer.d.ts.map +0 -1
- package/dist/components/renderers/stair/stair-renderer.js +0 -603
- package/dist/components/renderers/stair-segment/stair-segment-renderer.d.ts +0 -5
- package/dist/components/renderers/stair-segment/stair-segment-renderer.d.ts.map +0 -1
- package/dist/components/renderers/stair-segment/stair-segment-renderer.js +0 -32
- package/dist/components/renderers/wall/wall-renderer.d.ts +0 -5
- package/dist/components/renderers/wall/wall-renderer.d.ts.map +0 -1
- package/dist/components/renderers/wall/wall-renderer.js +0 -16
- package/dist/components/renderers/window/window-renderer.d.ts +0 -5
- package/dist/components/renderers/window/window-renderer.d.ts.map +0 -1
- package/dist/components/renderers/window/window-renderer.js +0 -21
- package/dist/components/renderers/zone/zone-renderer.d.ts +0 -5
- package/dist/components/renderers/zone/zone-renderer.d.ts.map +0 -1
- package/dist/components/renderers/zone/zone-renderer.js +0 -181
- package/dist/lib/suppress-three-clock-warning.d.ts +0 -25
- package/dist/lib/suppress-three-clock-warning.d.ts.map +0 -1
- package/dist/lib/suppress-three-clock-warning.js +0 -51
- package/dist/systems/export/export-system.d.ts +0 -2
- package/dist/systems/export/export-system.d.ts.map +0 -1
- package/dist/systems/export/export-system.js +0 -60
- package/dist/systems/item-mesh-metadata/compute-item-mesh-metadata.d.ts +0 -14
- package/dist/systems/item-mesh-metadata/compute-item-mesh-metadata.d.ts.map +0 -1
- package/dist/systems/item-mesh-metadata/compute-item-mesh-metadata.js +0 -155
- package/dist/systems/item-mesh-metadata/item-mesh-metadata-system.d.ts +0 -6
- package/dist/systems/item-mesh-metadata/item-mesh-metadata-system.d.ts.map +0 -1
- package/dist/systems/item-mesh-metadata/item-mesh-metadata-system.js +0 -75
- package/dist/systems/item-mesh-metadata/sync-request.d.ts +0 -7
- package/dist/systems/item-mesh-metadata/sync-request.d.ts.map +0 -1
- package/dist/systems/item-mesh-metadata/sync-request.js +0 -25
|
@@ -1,74 +1,201 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { sceneRegistry } from '@pascal-app/core';
|
|
2
3
|
import { useFrame } from '@react-three/fiber';
|
|
3
4
|
import { useMemo, useRef } from 'react';
|
|
4
5
|
import * as THREE from 'three/webgpu';
|
|
6
|
+
import { getSceneTheme } from '../../lib/scene-themes';
|
|
5
7
|
import useViewer from '../../store/use-viewer';
|
|
8
|
+
// Diagnostic toggle: `?disable=shadows` skips the shadow-map render pass
|
|
9
|
+
// (which doubles draw calls for every shadow-casting mesh) so you can
|
|
10
|
+
// isolate how much of the baseline GPU cost is shadows vs. raw geometry.
|
|
11
|
+
const SHADOWS_DISABLED = typeof window !== 'undefined' &&
|
|
12
|
+
new Set((new URLSearchParams(window.location.search).get('disable') ?? '')
|
|
13
|
+
.split(',')
|
|
14
|
+
.map((s) => s.trim())).has('shadows');
|
|
15
|
+
// Shadow darkness for the bright key lights (themes drive most lights past
|
|
16
|
+
// intensity 1). The aesthetic prototype runs these near-black (≈1.0); this is a
|
|
17
|
+
// deliberate middle ground — present, but not the heavy contact shadow there.
|
|
18
|
+
const MAX_SHADOW_INTENSITY = 0.55;
|
|
19
|
+
// Shadow frustum framing. The frustum is fit to the BUILDING geometry (not the
|
|
20
|
+
// camera): we union the bounds of all registered scene nodes, fit a sphere, and
|
|
21
|
+
// size the directional light's ortho shadow camera to that sphere plus a margin.
|
|
22
|
+
// This keeps shadows anchored to the building and a bit of surrounding ground no
|
|
23
|
+
// matter how the user zooms or pans — fixing the previous camera-following
|
|
24
|
+
// behaviour that fell apart when zoomed out (frustum too small) or zoomed into
|
|
25
|
+
// an empty corner (frustum centred on nothing).
|
|
26
|
+
//
|
|
27
|
+
// `site` nodes (the ground/site plane, which can be arbitrarily large) are
|
|
28
|
+
// excluded so they don't blow the frustum up to cover the whole lot.
|
|
29
|
+
const SHADOW_EXCLUDED_TYPES = ['site'];
|
|
30
|
+
// How often (seconds) to recompute building bounds. Bounds only change while
|
|
31
|
+
// editing, so we throttle the (subtree-walking) union instead of doing it every
|
|
32
|
+
// frame.
|
|
33
|
+
const BOUNDS_REFRESH_INTERVAL = 0.4;
|
|
34
|
+
// Extra coverage around the building bounds — the "and a bit nearby" margin so
|
|
35
|
+
// shadows don't get clipped right at the walls. Scales with building size.
|
|
36
|
+
const SHADOW_MARGIN_SCALE = 1.15;
|
|
37
|
+
const SHADOW_MARGIN = 3;
|
|
38
|
+
// Gap between the building bounds sphere and the light / near plane.
|
|
39
|
+
const SHADOW_BACKOFF = 10;
|
|
40
|
+
// Fallback radius when the scene has no building geometry yet (empty scene).
|
|
41
|
+
const SHADOW_FALLBACK_RADIUS = 30;
|
|
6
42
|
export function Lights() {
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
43
|
+
const sceneTheme = useViewer((state) => state.sceneTheme);
|
|
44
|
+
const theme = getSceneTheme(sceneTheme);
|
|
45
|
+
const shadows = useViewer((state) => state.shadows);
|
|
46
|
+
const lightRefs = useRef([]);
|
|
10
47
|
const shadowCamera = useRef(null);
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
|
|
48
|
+
// Initial ortho half-size; overridden each refresh to fit the building.
|
|
49
|
+
const shadowCameraSize = 50;
|
|
50
|
+
// Building bounds the shadow frustum is fit to, recomputed on an interval.
|
|
51
|
+
const shadowFocus = useRef(new THREE.Vector3()); // sphere centre
|
|
52
|
+
const shadowRadius = useRef(SHADOW_FALLBACK_RADIUS); // sphere radius
|
|
53
|
+
const shadowDir = useRef(new THREE.Vector3()); // scratch: per-light direction
|
|
54
|
+
const boundsBox = useRef(new THREE.Box3()); // scratch: union AABB
|
|
55
|
+
const boundsSphere = useRef(new THREE.Sphere()); // scratch: fitted sphere
|
|
56
|
+
const lastBoundsTime = useRef(-1); // last refresh timestamp (-1 = never)
|
|
57
|
+
const hemiRef = useRef(null);
|
|
14
58
|
const ambientRef = useRef(null);
|
|
15
59
|
const initialized = useRef(false);
|
|
60
|
+
const lightTargets = useRef([]);
|
|
16
61
|
const targets = useMemo(() => ({
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
l3Color: new THREE.Color(),
|
|
62
|
+
hemiSky: new THREE.Color(),
|
|
63
|
+
hemiGround: new THREE.Color(),
|
|
20
64
|
ambColor: new THREE.Color(),
|
|
21
65
|
}), []);
|
|
22
|
-
useFrame((
|
|
66
|
+
useFrame((state, delta) => {
|
|
23
67
|
// clamp delta to avoid huge jumps on tab switch
|
|
24
68
|
const dt = Math.min(delta, 0.1) * 4;
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
69
|
+
// Fit each shadow-casting light's frustum to the BUILDING geometry rather
|
|
70
|
+
// than the camera. We refresh the union bounds on an interval (cheap enough,
|
|
71
|
+
// and bounds only change while editing), fit a sphere, and size + place the
|
|
72
|
+
// ortho shadow camera so the building (plus a margin) is fully covered from
|
|
73
|
+
// the light's direction. The light DIRECTION stays exactly as the theme
|
|
74
|
+
// specifies; only its position/distance and the frustum extents change.
|
|
75
|
+
if (shadows) {
|
|
76
|
+
const now = state.clock.elapsedTime;
|
|
77
|
+
if (now - lastBoundsTime.current >= BOUNDS_REFRESH_INTERVAL) {
|
|
78
|
+
lastBoundsTime.current = now;
|
|
79
|
+
const box = boundsBox.current.makeEmpty();
|
|
80
|
+
for (const [id, obj] of sceneRegistry.nodes) {
|
|
81
|
+
if (SHADOW_EXCLUDED_TYPES.some((t) => sceneRegistry.byType[t].has(id)))
|
|
82
|
+
continue;
|
|
83
|
+
box.expandByObject(obj);
|
|
84
|
+
}
|
|
85
|
+
box.getBoundingSphere(boundsSphere.current);
|
|
86
|
+
const center = boundsSphere.current.center;
|
|
87
|
+
const radius = boundsSphere.current.radius;
|
|
88
|
+
// Empty scene OR a node with a NaN position/geometry poisoning the union
|
|
89
|
+
// box: fall back to the origin with a default radius. The directional
|
|
90
|
+
// light's position is derived from `focus`, so a single non-finite mesh
|
|
91
|
+
// must NOT be allowed to make `focus`/`radius` NaN — that breaks every
|
|
92
|
+
// shadow-casting light's position and renders the whole scene black.
|
|
93
|
+
const finiteBounds = !box.isEmpty() &&
|
|
94
|
+
Number.isFinite(center.x) &&
|
|
95
|
+
Number.isFinite(center.y) &&
|
|
96
|
+
Number.isFinite(center.z) &&
|
|
97
|
+
Number.isFinite(radius);
|
|
98
|
+
if (finiteBounds) {
|
|
99
|
+
shadowFocus.current.copy(center);
|
|
100
|
+
shadowRadius.current = radius;
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
shadowFocus.current.set(0, 0, 0);
|
|
104
|
+
shadowRadius.current = SHADOW_FALLBACK_RADIUS;
|
|
105
|
+
}
|
|
31
106
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
107
|
+
const focus = shadowFocus.current;
|
|
108
|
+
// Ortho half-extent: the building sphere plus a proportional margin.
|
|
109
|
+
const size = shadowRadius.current * SHADOW_MARGIN_SCALE + SHADOW_MARGIN;
|
|
110
|
+
// Park the light just outside the sphere so the near plane stays positive
|
|
111
|
+
// and the whole building fits between near and far along the light axis.
|
|
112
|
+
const distance = size + SHADOW_BACKOFF;
|
|
113
|
+
const near = SHADOW_BACKOFF;
|
|
114
|
+
const far = distance + size;
|
|
115
|
+
for (let index = 0; index < theme.lights.length; index++) {
|
|
116
|
+
const config = theme.lights[index];
|
|
117
|
+
const light = lightRefs.current[index];
|
|
118
|
+
if (!(config?.castShadow && light))
|
|
119
|
+
continue;
|
|
120
|
+
const [ox, oy, oz] = config.position;
|
|
121
|
+
const dir = shadowDir.current.set(ox, oy, oz);
|
|
122
|
+
if (dir.lengthSq() === 0)
|
|
123
|
+
dir.set(0, 1, 0);
|
|
124
|
+
dir.normalize().multiplyScalar(distance);
|
|
125
|
+
light.position.set(focus.x + dir.x, focus.y + dir.y, focus.z + dir.z);
|
|
126
|
+
light.target.position.copy(focus);
|
|
127
|
+
light.target.updateMatrixWorld();
|
|
128
|
+
// Resize the ortho frustum to the fitted bounds. The shadow camera is
|
|
129
|
+
// the <orthographicCamera attach="shadow-camera"> below.
|
|
130
|
+
const cam = light.shadow?.camera;
|
|
131
|
+
if (cam) {
|
|
132
|
+
cam.left = -size;
|
|
133
|
+
cam.right = size;
|
|
134
|
+
cam.top = size;
|
|
135
|
+
cam.bottom = -size;
|
|
136
|
+
cam.near = near;
|
|
137
|
+
cam.far = far;
|
|
138
|
+
cam.updateProjectionMatrix();
|
|
139
|
+
}
|
|
35
140
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
141
|
+
}
|
|
142
|
+
if (!initialized.current) {
|
|
143
|
+
for (let index = 0; index < theme.lights.length; index++) {
|
|
144
|
+
const config = theme.lights[index];
|
|
145
|
+
const light = lightRefs.current[index];
|
|
146
|
+
if (!(config && light))
|
|
147
|
+
continue;
|
|
148
|
+
light.intensity = config.intensity;
|
|
149
|
+
light.color.set(config.color);
|
|
150
|
+
if (config.castShadow && light.shadow) {
|
|
151
|
+
light.shadow.intensity = config.intensity <= 1 ? config.intensity : MAX_SHADOW_INTENSITY;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (hemiRef.current && theme.hemi) {
|
|
155
|
+
hemiRef.current.intensity = theme.hemi.intensity;
|
|
156
|
+
hemiRef.current.color.set(theme.hemi.sky);
|
|
157
|
+
hemiRef.current.groundColor.set(theme.hemi.ground);
|
|
39
158
|
}
|
|
40
159
|
if (ambientRef.current) {
|
|
41
|
-
ambientRef.current.intensity =
|
|
42
|
-
ambientRef.current.color.set(
|
|
160
|
+
ambientRef.current.intensity = theme.ambient.intensity;
|
|
161
|
+
ambientRef.current.color.set(theme.ambient.color);
|
|
43
162
|
}
|
|
44
163
|
initialized.current = true;
|
|
45
164
|
return;
|
|
46
165
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
166
|
+
for (let index = 0; index < theme.lights.length; index++) {
|
|
167
|
+
const config = theme.lights[index];
|
|
168
|
+
const light = lightRefs.current[index];
|
|
169
|
+
if (!(config && light))
|
|
170
|
+
continue;
|
|
171
|
+
light.intensity = THREE.MathUtils.lerp(light.intensity, config.intensity, dt);
|
|
172
|
+
let target = lightTargets.current[index];
|
|
173
|
+
if (!target) {
|
|
174
|
+
target = new THREE.Color();
|
|
175
|
+
lightTargets.current[index] = target;
|
|
176
|
+
}
|
|
177
|
+
target.set(config.color);
|
|
178
|
+
light.color.lerp(target, dt);
|
|
179
|
+
if (config.castShadow && light.shadow) {
|
|
180
|
+
if (light.shadow.intensity !== undefined) {
|
|
181
|
+
light.shadow.intensity = THREE.MathUtils.lerp(light.shadow.intensity, config.intensity <= 1 ? config.intensity : MAX_SHADOW_INTENSITY, dt);
|
|
54
182
|
}
|
|
55
183
|
}
|
|
56
184
|
}
|
|
57
|
-
if (
|
|
58
|
-
|
|
59
|
-
targets.
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
light3Ref.current.intensity = THREE.MathUtils.lerp(light3Ref.current.intensity, isDark ? 0.3 : 1, dt);
|
|
64
|
-
targets.l3Color.set(isDark ? '#a0b0ff' : '#ffffff');
|
|
65
|
-
light3Ref.current.color.lerp(targets.l3Color, dt);
|
|
185
|
+
if (hemiRef.current && theme.hemi) {
|
|
186
|
+
hemiRef.current.intensity = THREE.MathUtils.lerp(hemiRef.current.intensity, theme.hemi.intensity, dt);
|
|
187
|
+
targets.hemiSky.set(theme.hemi.sky);
|
|
188
|
+
hemiRef.current.color.lerp(targets.hemiSky, dt);
|
|
189
|
+
targets.hemiGround.set(theme.hemi.ground);
|
|
190
|
+
hemiRef.current.groundColor.lerp(targets.hemiGround, dt);
|
|
66
191
|
}
|
|
67
192
|
if (ambientRef.current) {
|
|
68
|
-
ambientRef.current.intensity = THREE.MathUtils.lerp(ambientRef.current.intensity,
|
|
69
|
-
targets.ambColor.set(
|
|
193
|
+
ambientRef.current.intensity = THREE.MathUtils.lerp(ambientRef.current.intensity, theme.ambient.intensity, dt);
|
|
194
|
+
targets.ambColor.set(theme.ambient.color);
|
|
70
195
|
ambientRef.current.color.lerp(targets.ambColor, dt);
|
|
71
196
|
}
|
|
72
197
|
});
|
|
73
|
-
return (_jsxs(_Fragment, { children: [
|
|
198
|
+
return (_jsxs(_Fragment, { children: [theme.lights.map((light, index) => (_jsx("directionalLight", { castShadow: Boolean(light.castShadow) && !SHADOWS_DISABLED && shadows, position: light.position, ref: (ref) => {
|
|
199
|
+
lightRefs.current[index] = ref;
|
|
200
|
+
}, "shadow-bias": -0.002, "shadow-mapSize": [1024, 1024], "shadow-normalBias": 0.3, "shadow-radius": 1.5, children: light.castShadow && !SHADOWS_DISABLED && shadows ? (_jsx("orthographicCamera", { attach: "shadow-camera", bottom: -shadowCameraSize, far: 400, left: -shadowCameraSize, near: 1, ref: shadowCamera, right: shadowCameraSize, top: shadowCameraSize })) : null }, `${index}-${light.position.join(',')}`))), theme.hemi ? _jsx("hemisphereLight", { ref: hemiRef }) : null, _jsx("ambientLight", { ref: ambientRef })] }));
|
|
74
201
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const PerfMonitor: () => import("react
|
|
1
|
+
export declare const PerfMonitor: () => import("react").JSX.Element;
|
|
2
2
|
//# sourceMappingURL=perf-monitor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"perf-monitor.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/perf-monitor.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"perf-monitor.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/perf-monitor.tsx"],"names":[],"mappings":"AAQA,eAAO,MAAM,WAAW,mCAuIvB,CAAA"}
|
|
@@ -1,15 +1,48 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useScene } from '@pascal-app/core';
|
|
3
3
|
import { Html } from '@react-three/drei';
|
|
4
|
-
import { useFrame } from '@react-three/fiber';
|
|
5
|
-
import { useRef, useState } from 'react';
|
|
4
|
+
import { useFrame, useThree } from '@react-three/fiber';
|
|
5
|
+
import { useEffect, useRef, useState } from 'react';
|
|
6
|
+
import { drainGpuSamples } from '../../lib/gpu-perf';
|
|
6
7
|
const SAMPLE_INTERVAL = 0.5; // seconds between display updates
|
|
7
8
|
export const PerfMonitor = () => {
|
|
8
|
-
const [stats, setStats] = useState({
|
|
9
|
+
const [stats, setStats] = useState({
|
|
10
|
+
fps: 0,
|
|
11
|
+
frameMs: 0,
|
|
12
|
+
gpuMs: 0,
|
|
13
|
+
gpuMaxMs: 0,
|
|
14
|
+
drawCalls: 0,
|
|
15
|
+
triangles: 0,
|
|
16
|
+
dirty: 0,
|
|
17
|
+
meshes: 0,
|
|
18
|
+
lines: 0,
|
|
19
|
+
sprites: 0,
|
|
20
|
+
lights: 0,
|
|
21
|
+
});
|
|
9
22
|
const frameCount = useRef(0);
|
|
10
23
|
const elapsed = useRef(0);
|
|
11
24
|
const lastMs = useRef(0);
|
|
12
|
-
|
|
25
|
+
// Carry the previous tick's reading forward when no fresh samples arrive,
|
|
26
|
+
// so the display doesn't flicker to "—" on slow resolve windows.
|
|
27
|
+
const lastGpuMs = useRef(0);
|
|
28
|
+
const lastGpuMaxMs = useRef(0);
|
|
29
|
+
// Take ownership of info reset. The custom RenderPipeline.render() path
|
|
30
|
+
// we use in post-processing doesn't trigger three.js's automatic per-frame
|
|
31
|
+
// info reset, so calls/triangles accumulate across frames and the display
|
|
32
|
+
// shows lifetime totals. Disabling autoReset and explicitly resetting at
|
|
33
|
+
// each window gives true per-frame averages.
|
|
34
|
+
const gl = useThree((s) => s.gl);
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
if (!gl?.info)
|
|
37
|
+
return;
|
|
38
|
+
const previousAutoReset = gl.info.autoReset;
|
|
39
|
+
gl.info.autoReset = false;
|
|
40
|
+
gl.info.reset();
|
|
41
|
+
return () => {
|
|
42
|
+
gl.info.autoReset = previousAutoReset;
|
|
43
|
+
};
|
|
44
|
+
}, [gl]);
|
|
45
|
+
useFrame(({ gl, scene, clock }) => {
|
|
13
46
|
frameCount.current++;
|
|
14
47
|
const now = clock.elapsedTime;
|
|
15
48
|
const dt = now - elapsed.current;
|
|
@@ -17,10 +50,60 @@ export const PerfMonitor = () => {
|
|
|
17
50
|
const fps = Math.round(frameCount.current / dt);
|
|
18
51
|
const frameMs = lastMs.current;
|
|
19
52
|
const info = gl.info;
|
|
20
|
-
|
|
21
|
-
|
|
53
|
+
// calls/triangles have been accumulating since the last reset (start of
|
|
54
|
+
// window). Divide by frameCount to get a per-frame average.
|
|
55
|
+
const totalCalls = info.render?.calls ?? 0;
|
|
56
|
+
const totalTriangles = info.render?.triangles ?? 0;
|
|
57
|
+
const drawCalls = Math.round(totalCalls / Math.max(1, frameCount.current));
|
|
58
|
+
const triangles = totalTriangles / Math.max(1, frameCount.current);
|
|
59
|
+
info.reset();
|
|
22
60
|
const dirty = useScene.getState().dirtyNodes.size;
|
|
23
|
-
|
|
61
|
+
// Count visible drawables by type so we can match scene contents
|
|
62
|
+
// against the renderer's draw count and find hidden contributors.
|
|
63
|
+
let meshes = 0;
|
|
64
|
+
let lines = 0;
|
|
65
|
+
let sprites = 0;
|
|
66
|
+
let lights = 0;
|
|
67
|
+
scene.traverse((obj) => {
|
|
68
|
+
if (!obj.visible)
|
|
69
|
+
return;
|
|
70
|
+
if (obj.isMesh)
|
|
71
|
+
meshes++;
|
|
72
|
+
else if (obj.isLine || obj.isLineSegments || obj.isLineLoop)
|
|
73
|
+
lines++;
|
|
74
|
+
else if (obj.isSprite)
|
|
75
|
+
sprites++;
|
|
76
|
+
else if (obj.isLight)
|
|
77
|
+
lights++;
|
|
78
|
+
});
|
|
79
|
+
// GPU samples are pushed by post-processing.tsx after each pipeline
|
|
80
|
+
// render via device.queue.onSubmittedWorkDone(). We drain whatever
|
|
81
|
+
// has accumulated since the last tick.
|
|
82
|
+
const samples = drainGpuSamples();
|
|
83
|
+
if (samples.length > 0) {
|
|
84
|
+
let sum = 0;
|
|
85
|
+
let max = 0;
|
|
86
|
+
for (const s of samples) {
|
|
87
|
+
sum += s;
|
|
88
|
+
if (s > max)
|
|
89
|
+
max = s;
|
|
90
|
+
}
|
|
91
|
+
lastGpuMs.current = sum / samples.length;
|
|
92
|
+
lastGpuMaxMs.current = max;
|
|
93
|
+
}
|
|
94
|
+
setStats({
|
|
95
|
+
fps,
|
|
96
|
+
frameMs,
|
|
97
|
+
gpuMs: lastGpuMs.current,
|
|
98
|
+
gpuMaxMs: lastGpuMaxMs.current,
|
|
99
|
+
drawCalls,
|
|
100
|
+
triangles,
|
|
101
|
+
dirty,
|
|
102
|
+
meshes,
|
|
103
|
+
lines,
|
|
104
|
+
sprites,
|
|
105
|
+
lights,
|
|
106
|
+
});
|
|
24
107
|
frameCount.current = 0;
|
|
25
108
|
elapsed.current = now;
|
|
26
109
|
}
|
|
@@ -35,8 +118,13 @@ export const PerfMonitor = () => {
|
|
|
35
118
|
borderRadius: 6,
|
|
36
119
|
padding: '6px 10px',
|
|
37
120
|
whiteSpace: 'pre',
|
|
38
|
-
}, children: `FPS
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
121
|
+
}, children: `FPS ${stats.fps}
|
|
122
|
+
GPU ${stats.gpuMs > 0 ? `${stats.gpuMs.toFixed(1)}ms (max ${stats.gpuMaxMs.toFixed(1)})` : '—'}
|
|
123
|
+
DRAW ${stats.drawCalls}
|
|
124
|
+
TRI ${(stats.triangles / 1000).toFixed(1)}k
|
|
125
|
+
DIRTY ${stats.dirty}
|
|
126
|
+
MESH ${stats.meshes}
|
|
127
|
+
LINE ${stats.lines}
|
|
128
|
+
SPRITE ${stats.sprites}
|
|
129
|
+
LIGHT ${stats.lights}` }) }));
|
|
42
130
|
};
|
|
@@ -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":"AAgCA,eAAO,MAAM,WAAW;;;;;;;;;;;;;CAavB,CAAA;AA0CD,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;AAiBD,QAAA,MAAM,oBAAoB,GAAI,kBAE3B;IACD,WAAW,CAAC,EAAE,WAAW,CAAA;CAC1B,SAgdA,CAAA;AAED,eAAe,oBAAoB,CAAA"}
|