@pascal-app/viewer 0.8.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.
Files changed (194) hide show
  1. package/dist/components/renderers/node-renderer.d.ts +1 -1
  2. package/dist/components/renderers/node-renderer.d.ts.map +1 -1
  3. package/dist/components/renderers/node-renderer.js +38 -22
  4. package/dist/components/renderers/parametric-node-renderer.d.ts +5 -0
  5. package/dist/components/renderers/parametric-node-renderer.d.ts.map +1 -0
  6. package/dist/components/renderers/parametric-node-renderer.js +34 -0
  7. package/dist/components/renderers/scene-renderer.d.ts +1 -1
  8. package/dist/components/renderers/scene-renderer.d.ts.map +1 -1
  9. package/dist/components/viewer/ground-occluder.d.ts +1 -1
  10. package/dist/components/viewer/ground-occluder.d.ts.map +1 -1
  11. package/dist/components/viewer/ground-occluder.js +12 -24
  12. package/dist/components/viewer/index.d.ts +37 -1
  13. package/dist/components/viewer/index.d.ts.map +1 -1
  14. package/dist/components/viewer/index.js +186 -63
  15. package/dist/components/viewer/lights.d.ts +1 -1
  16. package/dist/components/viewer/lights.d.ts.map +1 -1
  17. package/dist/components/viewer/lights.js +170 -43
  18. package/dist/components/viewer/perf-monitor.d.ts +1 -1
  19. package/dist/components/viewer/perf-monitor.d.ts.map +1 -1
  20. package/dist/components/viewer/perf-monitor.js +99 -11
  21. package/dist/components/viewer/post-processing.d.ts.map +1 -1
  22. package/dist/components/viewer/post-processing.js +231 -54
  23. package/dist/components/viewer/registered-systems.d.ts +12 -0
  24. package/dist/components/viewer/registered-systems.d.ts.map +1 -0
  25. package/dist/components/viewer/registered-systems.js +47 -0
  26. package/dist/components/viewer/scene-bvh.d.ts +17 -0
  27. package/dist/components/viewer/scene-bvh.d.ts.map +1 -0
  28. package/dist/components/viewer/scene-bvh.js +94 -0
  29. package/dist/components/viewer/selection-manager.d.ts +1 -1
  30. package/dist/components/viewer/selection-manager.d.ts.map +1 -1
  31. package/dist/components/viewer/selection-manager.js +20 -5
  32. package/dist/components/viewer/viewer-camera.d.ts +1 -1
  33. package/dist/components/viewer/viewer-camera.d.ts.map +1 -1
  34. package/dist/components/viewer/walkthrough-controls.d.ts +1 -1
  35. package/dist/components/viewer/walkthrough-controls.d.ts.map +1 -1
  36. package/dist/hooks/use-gltf-ktx2.d.ts.map +1 -1
  37. package/dist/hooks/use-node-events.d.ts +5 -73
  38. package/dist/hooks/use-node-events.d.ts.map +1 -1
  39. package/dist/hooks/use-node-events.js +19 -7
  40. package/dist/index.d.ts +36 -4
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +67 -3
  43. package/dist/lib/csg-utils.d.ts +16 -0
  44. package/dist/lib/csg-utils.d.ts.map +1 -0
  45. package/dist/lib/csg-utils.js +33 -0
  46. package/dist/lib/drawable-geometry.d.ts +21 -0
  47. package/dist/lib/drawable-geometry.d.ts.map +1 -0
  48. package/dist/lib/drawable-geometry.js +22 -0
  49. package/dist/lib/edge-style.d.ts +3 -0
  50. package/dist/lib/edge-style.d.ts.map +1 -0
  51. package/dist/lib/edge-style.js +11 -0
  52. package/dist/lib/gpu-perf.d.ts +4 -0
  53. package/dist/lib/gpu-perf.d.ts.map +1 -0
  54. package/dist/lib/gpu-perf.js +23 -0
  55. package/dist/lib/ink-edges.d.ts +9 -0
  56. package/dist/lib/ink-edges.d.ts.map +1 -0
  57. package/dist/lib/ink-edges.js +43 -0
  58. package/dist/lib/isolation.d.ts +39 -0
  59. package/dist/lib/isolation.d.ts.map +1 -0
  60. package/dist/lib/isolation.js +103 -0
  61. package/dist/lib/layers.d.ts +18 -0
  62. package/dist/lib/layers.d.ts.map +1 -1
  63. package/dist/lib/layers.js +18 -0
  64. package/dist/lib/materials.d.ts +25 -15
  65. package/dist/lib/materials.d.ts.map +1 -1
  66. package/dist/lib/materials.js +277 -62
  67. package/dist/lib/merged-outline-node.d.ts.map +1 -1
  68. package/dist/lib/merged-outline-node.js +7 -0
  69. package/dist/lib/polygon-union.d.ts +4 -0
  70. package/dist/lib/polygon-union.d.ts.map +1 -0
  71. package/dist/lib/polygon-union.js +289 -0
  72. package/dist/lib/polygon-union.test.d.ts +2 -0
  73. package/dist/lib/polygon-union.test.d.ts.map +1 -0
  74. package/dist/lib/polygon-union.test.js +105 -0
  75. package/dist/lib/scene-themes.d.ts +29 -0
  76. package/dist/lib/scene-themes.d.ts.map +1 -0
  77. package/dist/lib/scene-themes.js +193 -0
  78. package/dist/store/use-viewer.d.ts +42 -4
  79. package/dist/store/use-viewer.d.ts.map +1 -1
  80. package/dist/store/use-viewer.js +26 -3
  81. package/dist/systems/ceiling/ceiling-system.d.ts +6 -2
  82. package/dist/systems/ceiling/ceiling-system.d.ts.map +1 -1
  83. package/dist/systems/ceiling/ceiling-system.js +62 -12
  84. package/dist/systems/door/door-system.d.ts.map +1 -1
  85. package/dist/systems/door/door-system.js +373 -22
  86. package/dist/systems/elevator/elevator-interaction-system.d.ts +2 -0
  87. package/dist/systems/elevator/elevator-interaction-system.d.ts.map +1 -0
  88. package/dist/systems/elevator/elevator-interaction-system.js +62 -0
  89. package/dist/systems/fence/fence-system.d.ts +3 -0
  90. package/dist/systems/fence/fence-system.d.ts.map +1 -1
  91. package/dist/systems/fence/fence-system.js +9 -5
  92. package/dist/systems/floor-elevation/floor-elevation-system.d.ts +20 -0
  93. package/dist/systems/floor-elevation/floor-elevation-system.d.ts.map +1 -0
  94. package/dist/systems/floor-elevation/floor-elevation-system.js +78 -0
  95. package/dist/systems/geometry/geometry-system.d.ts +34 -0
  96. package/dist/systems/geometry/geometry-system.d.ts.map +1 -0
  97. package/dist/systems/geometry/geometry-system.js +279 -0
  98. package/dist/systems/interactive/interactive-system.d.ts +1 -1
  99. package/dist/systems/interactive/interactive-system.d.ts.map +1 -1
  100. package/dist/systems/item/item-system.d.ts +7 -0
  101. package/dist/systems/item/item-system.d.ts.map +1 -1
  102. package/dist/systems/item/item-system.js +8 -11
  103. package/dist/systems/item-light/item-light-system.d.ts +1 -1
  104. package/dist/systems/item-light/item-light-system.d.ts.map +1 -1
  105. package/dist/systems/item-light/item-light-system.js +18 -5
  106. package/dist/systems/roof/roof-materials.d.ts +3 -2
  107. package/dist/systems/roof/roof-materials.d.ts.map +1 -1
  108. package/dist/systems/roof/roof-materials.js +38 -14
  109. package/dist/systems/roof/roof-system.d.ts +27 -0
  110. package/dist/systems/roof/roof-system.d.ts.map +1 -1
  111. package/dist/systems/roof/roof-system.js +396 -59
  112. package/dist/systems/slab/slab-system.d.ts.map +1 -1
  113. package/dist/systems/slab/slab-system.js +167 -49
  114. package/dist/systems/slab/slab-system.test.d.ts +2 -0
  115. package/dist/systems/slab/slab-system.test.d.ts.map +1 -0
  116. package/dist/systems/slab/slab-system.test.js +62 -0
  117. package/dist/systems/stair/stair-materials.d.ts +4 -3
  118. package/dist/systems/stair/stair-materials.d.ts.map +1 -1
  119. package/dist/systems/stair/stair-materials.js +30 -15
  120. package/dist/systems/stair/stair-system.d.ts.map +1 -1
  121. package/dist/systems/stair/stair-system.js +50 -61
  122. package/dist/systems/surface-hole-geometry.d.ts +3 -0
  123. package/dist/systems/surface-hole-geometry.d.ts.map +1 -0
  124. package/dist/systems/surface-hole-geometry.js +4 -0
  125. package/dist/systems/wall/wall-cutout.d.ts.map +1 -1
  126. package/dist/systems/wall/wall-cutout.js +18 -2
  127. package/dist/systems/wall/wall-materials.d.ts +4 -3
  128. package/dist/systems/wall/wall-materials.d.ts.map +1 -1
  129. package/dist/systems/wall/wall-materials.js +52 -28
  130. package/dist/systems/wall/wall-system.d.ts.map +1 -1
  131. package/dist/systems/wall/wall-system.js +171 -26
  132. package/dist/systems/window/window-system.d.ts.map +1 -1
  133. package/dist/systems/window/window-system.js +71 -7
  134. package/package.json +3 -4
  135. package/dist/components/renderers/building/building-renderer.d.ts +0 -5
  136. package/dist/components/renderers/building/building-renderer.d.ts.map +0 -1
  137. package/dist/components/renderers/building/building-renderer.js +0 -11
  138. package/dist/components/renderers/ceiling/ceiling-renderer.d.ts +0 -5
  139. package/dist/components/renderers/ceiling/ceiling-renderer.d.ts.map +0 -1
  140. package/dist/components/renderers/ceiling/ceiling-renderer.js +0 -58
  141. package/dist/components/renderers/column/column-renderer.d.ts +0 -5
  142. package/dist/components/renderers/column/column-renderer.d.ts.map +0 -1
  143. package/dist/components/renderers/column/column-renderer.js +0 -597
  144. package/dist/components/renderers/door/door-renderer.d.ts +0 -5
  145. package/dist/components/renderers/door/door-renderer.d.ts.map +0 -1
  146. package/dist/components/renderers/door/door-renderer.js +0 -16
  147. package/dist/components/renderers/fence/fence-renderer.d.ts +0 -5
  148. package/dist/components/renderers/fence/fence-renderer.d.ts.map +0 -1
  149. package/dist/components/renderers/fence/fence-renderer.js +0 -15
  150. package/dist/components/renderers/guide/guide-renderer.d.ts +0 -5
  151. package/dist/components/renderers/guide/guide-renderer.d.ts.map +0 -1
  152. package/dist/components/renderers/guide/guide-renderer.js +0 -38
  153. package/dist/components/renderers/item/item-renderer.d.ts +0 -5
  154. package/dist/components/renderers/item/item-renderer.d.ts.map +0 -1
  155. package/dist/components/renderers/item/item-renderer.js +0 -172
  156. package/dist/components/renderers/level/level-renderer.d.ts +0 -5
  157. package/dist/components/renderers/level/level-renderer.d.ts.map +0 -1
  158. package/dist/components/renderers/level/level-renderer.js +0 -11
  159. package/dist/components/renderers/roof/roof-materials.d.ts +0 -4
  160. package/dist/components/renderers/roof/roof-materials.d.ts.map +0 -1
  161. package/dist/components/renderers/roof/roof-materials.js +0 -16
  162. package/dist/components/renderers/roof/roof-renderer.d.ts +0 -5
  163. package/dist/components/renderers/roof/roof-renderer.d.ts.map +0 -1
  164. package/dist/components/renderers/roof/roof-renderer.js +0 -32
  165. package/dist/components/renderers/roof-segment/roof-segment-renderer.d.ts +0 -5
  166. package/dist/components/renderers/roof-segment/roof-segment-renderer.d.ts.map +0 -1
  167. package/dist/components/renderers/roof-segment/roof-segment-renderer.js +0 -40
  168. package/dist/components/renderers/scan/scan-renderer.d.ts +0 -5
  169. package/dist/components/renderers/scan/scan-renderer.d.ts.map +0 -1
  170. package/dist/components/renderers/scan/scan-renderer.js +0 -54
  171. package/dist/components/renderers/site/site-renderer.d.ts +0 -5
  172. package/dist/components/renderers/site/site-renderer.d.ts.map +0 -1
  173. package/dist/components/renderers/site/site-renderer.js +0 -106
  174. package/dist/components/renderers/slab/slab-renderer.d.ts +0 -5
  175. package/dist/components/renderers/slab/slab-renderer.d.ts.map +0 -1
  176. package/dist/components/renderers/slab/slab-renderer.js +0 -58
  177. package/dist/components/renderers/spawn/spawn-renderer.d.ts +0 -5
  178. package/dist/components/renderers/spawn/spawn-renderer.d.ts.map +0 -1
  179. package/dist/components/renderers/spawn/spawn-renderer.js +0 -32
  180. package/dist/components/renderers/stair/stair-renderer.d.ts +0 -5
  181. package/dist/components/renderers/stair/stair-renderer.d.ts.map +0 -1
  182. package/dist/components/renderers/stair/stair-renderer.js +0 -603
  183. package/dist/components/renderers/stair-segment/stair-segment-renderer.d.ts +0 -5
  184. package/dist/components/renderers/stair-segment/stair-segment-renderer.d.ts.map +0 -1
  185. package/dist/components/renderers/stair-segment/stair-segment-renderer.js +0 -51
  186. package/dist/components/renderers/wall/wall-renderer.d.ts +0 -5
  187. package/dist/components/renderers/wall/wall-renderer.d.ts.map +0 -1
  188. package/dist/components/renderers/wall/wall-renderer.js +0 -16
  189. package/dist/components/renderers/window/window-renderer.d.ts +0 -5
  190. package/dist/components/renderers/window/window-renderer.d.ts.map +0 -1
  191. package/dist/components/renderers/window/window-renderer.js +0 -21
  192. package/dist/components/renderers/zone/zone-renderer.d.ts +0 -5
  193. package/dist/components/renderers/zone/zone-renderer.d.ts.map +0 -1
  194. package/dist/components/renderers/zone/zone-renderer.js +0 -181
@@ -5,8 +5,12 @@ import { ssgi } from 'three/addons/tsl/display/SSGINode.js';
5
5
  import { denoise } from 'three/examples/jsm/tsl/display/DenoiseNode.js';
6
6
  import { add, colorToDirection, diffuseColor, directionToColor, float, mix, mrt, normalView, oscSine, output, pass, sample, time, uniform, vec4, } from 'three/tsl';
7
7
  import { RenderPipeline } from 'three/webgpu';
8
- import { SCENE_LAYER, ZONE_LAYER } from '../../lib/layers';
8
+ import { edgeColorFor } from '../../lib/edge-style';
9
+ import { PERF_OVERLAY_ENABLED, pushGpuSample } from '../../lib/gpu-perf';
10
+ import { inkedEdges } from '../../lib/ink-edges';
11
+ import { GRID_LAYER, OVERLAY_LAYER, SCENE_LAYER, ZONE_LAYER } from '../../lib/layers';
9
12
  import { mergedOutline } from '../../lib/merged-outline-node';
13
+ import { getSceneTheme } from '../../lib/scene-themes';
10
14
  import useViewer from '../../store/use-viewer';
11
15
  // SSGI Parameters - adjust these to fine-tune global illumination and ambient occlusion
12
16
  export const SSGI_PARAMS = {
@@ -23,10 +27,38 @@ export const SSGI_PARAMS = {
23
27
  useScreenSpaceSampling: true,
24
28
  useTemporalFiltering: false,
25
29
  };
30
+ // Diagnostic toggles for thermal A/B testing. Add `?disable=ao,denoise,outline,postFx`
31
+ // to the URL (any subset) and reload to skip those passes. Each flag prevents
32
+ // allocation + per-frame work for that stage, so device temperature deltas
33
+ // across combos isolate which pass is the actual culprit. Picked up once at
34
+ // pipeline build; reload after changing the URL.
35
+ // - ao: skip SSGI entirely (and denoise, since denoise has nothing to denoise)
36
+ // - denoise: keep SSGI but feed its raw noisy AO straight to the composite
37
+ // - outline: skip the merged-outline node and its 14 internal RTs
38
+ // - postFx: bypass the whole RenderPipeline and use renderer.render(scene, camera)
39
+ // directly — isolates raw scene-render cost from any post-FX overhead
40
+ function readPerfDisableFlags() {
41
+ if (typeof window === 'undefined') {
42
+ return { ao: false, denoise: false, outline: false, postFx: false };
43
+ }
44
+ const raw = new URLSearchParams(window.location.search).get('disable') ?? '';
45
+ const set = new Set(raw
46
+ .split(',')
47
+ .map((s) => s.trim())
48
+ .filter(Boolean));
49
+ return {
50
+ ao: set.has('ao'),
51
+ denoise: set.has('denoise'),
52
+ outline: set.has('outline'),
53
+ postFx: set.has('postFx'),
54
+ };
55
+ }
56
+ const PERF_POST_FX_DISABLED = typeof window !== 'undefined' &&
57
+ new Set((new URLSearchParams(window.location.search).get('disable') ?? '')
58
+ .split(',')
59
+ .map((s) => s.trim())).has('postFx');
26
60
  const MAX_PIPELINE_RETRIES = 3;
27
61
  const RETRY_DELAY_MS = 500;
28
- const DARK_BG = '#1f2433';
29
- const LIGHT_BG = '#ffffff';
30
62
  const DEFAULT_HOVER_STYLE = {
31
63
  visibleColor: 0x00_aa_ff,
32
64
  hiddenColor: 0xf3_ff_47,
@@ -48,23 +80,46 @@ function sanitizeOutlineObjects(objects) {
48
80
  objects.length = nextIndex;
49
81
  }
50
82
  const PostProcessingPasses = ({ hoverStyles = DEFAULT_HOVER_STYLES, }) => {
51
- const { gl: renderer, invalidate, scene, camera } = useThree();
83
+ const { gl: renderer, invalidate, scene, camera, size } = useThree();
52
84
  const renderPipelineRef = useRef(null);
53
85
  const hasPipelineErrorRef = useRef(false);
54
86
  const retryCountRef = useRef(0);
55
87
  const rebuildTimeoutRef = useRef(null);
88
+ const skippedZeroSizeRef = useRef(false);
56
89
  // Background color uniform — updated every frame via lerp, read by the TSL pipeline.
57
- // Initialised from the current theme so there's no flash on first render.
58
- const initBg = useViewer.getState().theme === 'dark' ? DARK_BG : LIGHT_BG;
90
+ // Initialised from the current scene theme so there's no flash on first render.
91
+ const initBg = getSceneTheme(useViewer.getState().sceneTheme).background;
59
92
  const bgUniform = useRef(uniform(new Color(initBg)));
60
93
  const bgCurrent = useRef(new Color(initBg));
61
94
  const bgTarget = useRef(new Color());
95
+ // Ink-line colour follows the scene-theme background luminance (dark lines on
96
+ // light scenes, light on dark), refreshed each frame like the background.
97
+ const inkColorUniform = useRef(uniform(new Color(edgeColorFor(initBg))));
62
98
  const zoneLayers = useMemo(() => {
63
99
  const l = new Layers();
64
100
  l.enable(ZONE_LAYER);
65
101
  l.disable(SCENE_LAYER);
66
102
  return l;
67
103
  }, []);
104
+ // Scene pass renders the main geometry layer plus the grid. The default camera
105
+ // mask also has the overlay layer enabled (custom controls enable it for
106
+ // picking), so without this the gizmos/handles/tool previews land in the
107
+ // depth+normal MRT and get inked / AO'd as if they were geometry. The grid is
108
+ // kept in here (not the overlay pass) so scene geometry depth-occludes it; it's
109
+ // a flat, depth-non-writing plane so the ink never picks it up.
110
+ const sceneOnlyLayers = useMemo(() => {
111
+ const l = new Layers();
112
+ l.set(SCENE_LAYER);
113
+ l.enable(GRID_LAYER);
114
+ return l;
115
+ }, []);
116
+ // Editor overlays render in their own pass, composited on top after the ink
117
+ // and outlines so they read as crisp UI rather than scene geometry.
118
+ const overlayLayers = useMemo(() => {
119
+ const l = new Layers();
120
+ l.set(OVERLAY_LAYER);
121
+ return l;
122
+ }, []);
68
123
  const hoverHighlightMode = useViewer((s) => s.hoverHighlightMode);
69
124
  const hoverVisibleColor = useMemo(() => uniform(new Color(DEFAULT_HOVER_STYLE.visibleColor)), []);
70
125
  const hoverHiddenColor = useMemo(() => uniform(new Color(DEFAULT_HOVER_STYLE.hiddenColor)), []);
@@ -72,6 +127,9 @@ const PostProcessingPasses = ({ hoverStyles = DEFAULT_HOVER_STYLES, }) => {
72
127
  const hoverPulseMix = useMemo(() => uniform(DEFAULT_HOVER_STYLE.pulse ? 0 : 1), []);
73
128
  // Subscribe to projectId so the pipeline rebuilds on project switch
74
129
  const projectId = useViewer((s) => s.projectId);
130
+ const shading = useViewer((s) => s.shading);
131
+ const edges = useViewer((s) => s.edges);
132
+ const lastProjectIdRef = useRef(projectId);
75
133
  // Bump this to force a pipeline rebuild (used by retry logic)
76
134
  const [pipelineVersion, setPipelineVersion] = useState(0);
77
135
  const requestPipelineRebuild = useCallback(() => {
@@ -83,6 +141,9 @@ const PostProcessingPasses = ({ hoverStyles = DEFAULT_HOVER_STYLES, }) => {
83
141
  }, []);
84
142
  // Reset retry state when project changes
85
143
  useEffect(() => {
144
+ if (lastProjectIdRef.current === projectId)
145
+ return;
146
+ lastProjectIdRef.current = projectId;
86
147
  retryCountRef.current = 0;
87
148
  if (rebuildTimeoutRef.current !== null) {
88
149
  clearTimeout(rebuildTimeoutRef.current);
@@ -115,6 +176,8 @@ const PostProcessingPasses = ({ hoverStyles = DEFAULT_HOVER_STYLES, }) => {
115
176
  ]);
116
177
  // Build / rebuild the post-processing pipeline
117
178
  useEffect(() => {
179
+ const width = Math.floor(size.width);
180
+ const height = Math.floor(size.height);
118
181
  if (!(renderer && scene && camera)) {
119
182
  console.warn('[viewer/post-processing] Skipping pipeline build — missing dependency.', {
120
183
  hasRenderer: !!renderer,
@@ -123,14 +186,60 @@ const PostProcessingPasses = ({ hoverStyles = DEFAULT_HOVER_STYLES, }) => {
123
186
  });
124
187
  return;
125
188
  }
189
+ if (width < 1 || height < 1) {
190
+ skippedZeroSizeRef.current = true;
191
+ hasPipelineErrorRef.current = false;
192
+ if (renderPipelineRef.current) {
193
+ renderPipelineRef.current.dispose();
194
+ }
195
+ renderPipelineRef.current = null;
196
+ return;
197
+ }
198
+ if (skippedZeroSizeRef.current) {
199
+ skippedZeroSizeRef.current = false;
200
+ }
201
+ const perfDisable = readPerfDisableFlags();
202
+ const ssgiEnabled = shading === 'rendered' && SSGI_PARAMS.enabled && !perfDisable.ao;
203
+ const denoiseEnabled = ssgiEnabled && !perfDisable.denoise;
204
+ const outlineEnabled = !perfDisable.outline;
205
+ const inkEnabled = edges !== 'off';
206
+ // The depth+normal MRT feeds both SSGI and the screen-space ink pass.
207
+ const needsNormalMRT = ssgiEnabled || inkEnabled;
208
+ // Soft = thin (1px sample radius) + faint (50% opacity); strong = thick
209
+ // (2px, ~2× wider detected band) + solid (100%). The edge masks saturate,
210
+ // so radius+opacity are what actually separate the two modes — gain wouldn't.
211
+ // Same 1px line thickness for both (soft's thickness is the nice one);
212
+ // strong reads heavier purely by being fully solid vs soft's lighter 50%.
213
+ const inkRadius = 1;
214
+ const inkOpacity = edges === 'strong' ? 1 : 0.5;
126
215
  console.log('[viewer/post-processing] Building pipeline', {
127
216
  version: pipelineVersion,
128
- ssgi: SSGI_PARAMS.enabled,
217
+ ssgi: ssgiEnabled,
218
+ denoise: denoiseEnabled,
219
+ outline: outlineEnabled,
220
+ perfDisable,
129
221
  hoverHighlightMode,
130
222
  projectId,
223
+ shading,
131
224
  rendererCtor: renderer.constructor?.name,
225
+ width,
226
+ height,
132
227
  });
133
228
  hasPipelineErrorRef.current = false;
229
+ // WebGPU availability check: SSGI, denoise, and RenderPipeline are all
230
+ // WebGPU-only APIs. When the browser falls back to WebGL2 (no
231
+ // `navigator.gpu`, or the device couldn't be created), building the
232
+ // pipeline either throws silently or produces a broken output where
233
+ // the scene renders for a few frames and then goes black as the retry
234
+ // loop fights the direct-render fallback path. Short-circuit here so
235
+ // `useFrame` uses the direct `renderer.render(scene, camera)` path
236
+ // exclusively and never attempts the TSL pipeline.
237
+ const hasWebGPU = typeof navigator !== 'undefined' && 'gpu' in navigator;
238
+ if (!hasWebGPU) {
239
+ hasPipelineErrorRef.current = true;
240
+ renderPipelineRef.current = null;
241
+ return;
242
+ }
134
243
  // Clear outliner arrays synchronously to prevent stale Object3D refs
135
244
  // from the previous project leaking into the new pipeline's outline passes.
136
245
  const outliner = useViewer.getState().outliner;
@@ -140,8 +249,15 @@ const PostProcessingPasses = ({ hoverStyles = DEFAULT_HOVER_STYLES, }) => {
140
249
  outliner.hoveredObjects.length = 0;
141
250
  try {
142
251
  const scenePass = pass(scene, camera);
252
+ scenePass.setLayers(sceneOnlyLayers);
143
253
  const zonePass = pass(scene, camera);
144
254
  zonePass.setLayers(zoneLayers);
255
+ // Editor overlays (gizmos, move handles, tool previews, grid) on their own
256
+ // layer, kept out of the depth/normal MRT above so the ink + SSGI ignore
257
+ // them, then composited on top of the final image below.
258
+ const overlayPass = pass(scene, camera);
259
+ overlayPass.setLayers(overlayLayers);
260
+ const overlayColor = overlayPass.getTextureNode('output');
145
261
  const scenePassColor = scenePass.getTextureNode('output');
146
262
  // Background detection via alpha: renderer clears with alpha=0 (setClearAlpha(0) in useFrame),
147
263
  // so background pixels have scenePassColor.a=0 while geometry pixels have output.a=1.
@@ -150,23 +266,28 @@ const PostProcessingPasses = ({ hoverStyles = DEFAULT_HOVER_STYLES, }) => {
150
266
  const hasGeometry = scenePassColor.a;
151
267
  const contentAlpha = hasGeometry.max(zonePass.a);
152
268
  let sceneColor = scenePassColor;
153
- if (SSGI_PARAMS.enabled) {
154
- // MRT only needed for SSGI (diffuse for GI, normal for SSGI sampling)
269
+ // Depth + normal MRT — shared by SSGI (diffuse/normal) and the ink pass
270
+ // (depth/normal). Built whenever either is active.
271
+ let scenePassDepth = null;
272
+ let scenePassNormal = null;
273
+ let sceneNormal = null;
274
+ if (needsNormalMRT) {
155
275
  scenePass.setMRT(mrt({
156
276
  output,
157
277
  diffuseColor,
158
278
  normal: directionToColor(normalView),
159
279
  }));
280
+ scenePassDepth = scenePass.getTextureNode('depth');
281
+ scenePassNormal = scenePass.getTextureNode('normal');
282
+ const normalTexture = scenePass.getTexture('normal');
283
+ normalTexture.type = UnsignedByteType;
284
+ // Extract normal from color-encoded texture (SSGI consumes the node form)
285
+ sceneNormal = sample((uv) => colorToDirection(scenePassNormal.sample(uv)));
286
+ }
287
+ if (ssgiEnabled) {
160
288
  const scenePassDiffuse = scenePass.getTextureNode('diffuseColor');
161
- const scenePassDepth = scenePass.getTextureNode('depth');
162
- const scenePassNormal = scenePass.getTextureNode('normal');
163
- // Optimize texture bandwidth
164
289
  const diffuseTexture = scenePass.getTexture('diffuseColor');
165
290
  diffuseTexture.type = UnsignedByteType;
166
- const normalTexture = scenePass.getTexture('normal');
167
- normalTexture.type = UnsignedByteType;
168
- // Extract normal from color-encoded texture
169
- const sceneNormal = sample((uv) => colorToDirection(scenePassNormal.sample(uv)));
170
291
  const giPass = ssgi(scenePassColor, scenePassDepth, sceneNormal, camera);
171
292
  giPass.sliceCount.value = SSGI_PARAMS.sliceCount;
172
293
  giPass.stepCount.value = SSGI_PARAMS.stepCount;
@@ -180,49 +301,76 @@ const PostProcessingPasses = ({ hoverStyles = DEFAULT_HOVER_STYLES, }) => {
180
301
  giPass.useScreenSpaceSampling.value = SSGI_PARAMS.useScreenSpaceSampling;
181
302
  giPass.useTemporalFiltering = SSGI_PARAMS.useTemporalFiltering;
182
303
  const giTexture = giPass.getTextureNode();
183
- // DenoiseNode only denoises RGB — alpha is passed through unchanged.
184
- // SSGI packs AO into alpha, so we remap it into RGB before denoising.
185
- const aoAsRgb = vec4(giTexture.a, giTexture.a, giTexture.a, float(1));
186
- const denoisePass = denoise(aoAsRgb, scenePassDepth, sceneNormal, camera);
187
- denoisePass.index.value = 0;
188
- denoisePass.radius.value = 4;
189
304
  const gi = giPass.rgb;
190
- const ao = denoisePass.r;
305
+ let ao;
306
+ if (denoiseEnabled) {
307
+ // DenoiseNode only denoises RGB — alpha is passed through unchanged.
308
+ // SSGI packs AO into alpha, so we remap it into RGB before denoising.
309
+ const aoAsRgb = vec4(giTexture.a, giTexture.a, giTexture.a, float(1));
310
+ const denoisePass = denoise(aoAsRgb, scenePassDepth, sceneNormal, camera);
311
+ denoisePass.index.value = 0;
312
+ denoisePass.radius.value = 4;
313
+ ao = denoisePass.r;
314
+ }
315
+ else {
316
+ // Diagnostic path: feed raw noisy SSGI AO straight through. Will
317
+ // look grainy — that's the point, it isolates denoise cost.
318
+ ao = giTexture.a;
319
+ }
191
320
  // Composite: scene * AO + diffuse * GI
192
321
  sceneColor = vec4(add(scenePassColor.rgb.mul(ao), add(zonePass.rgb, scenePassDiffuse.rgb.mul(gi))), contentAlpha);
193
322
  }
323
+ // Screen-space ink outline (SketchUp look) — depth/normal edge detection
324
+ // over the composited scene. Topology-agnostic, so it handles CSG-cut
325
+ // walls cleanly. Applied before the selection outline + background mix.
326
+ if (inkEnabled) {
327
+ sceneColor = vec4(inkedEdges({
328
+ sceneRgb: sceneColor.rgb,
329
+ depthTex: scenePassDepth,
330
+ normalTex: scenePassNormal,
331
+ inkColor: inkColorUniform.current,
332
+ radius: inkRadius,
333
+ opacity: inkOpacity,
334
+ }), sceneColor.a);
335
+ }
194
336
  // Single merged outline node: one shared depth pass for both selected + hovered groups.
195
337
  const outliner = useViewer.getState().outliner;
196
- const outlineNode = mergedOutline(scene, camera, {
197
- primaryObjects: outliner.selectedObjects,
198
- secondaryObjects: outliner.hoveredObjects,
199
- primaryEdgeThickness: uniform(1),
200
- secondaryEdgeThickness: uniform(1.5),
201
- });
202
- // Selected: white visible, yellow hidden
203
- const selectedVisibleColor = uniform(new Color(0xff_ff_ff));
204
- const selectedHiddenColor = uniform(new Color(0xf3_ff_47));
205
- const selectedStrength = uniform(3);
206
- const selectedOutline = outlineNode.primaryVisibleEdge
207
- .mul(selectedVisibleColor)
208
- .add(outlineNode.primaryHiddenEdge.mul(selectedHiddenColor))
209
- .mul(selectedStrength);
210
- // Hovered: blue visible, yellow hidden, pulsing
211
- const pulsePeriod = uniform(3);
212
- const oscillating = oscSine(time.div(pulsePeriod).mul(2)).mul(0.5).add(0.5);
213
- const osc = mix(oscillating, float(1), hoverPulseMix);
214
- const hoverOutline = outlineNode.secondaryVisibleEdge
215
- .mul(hoverVisibleColor)
216
- .add(outlineNode.secondaryHiddenEdge.mul(hoverHiddenColor))
217
- .mul(hoverStrength)
218
- .mul(osc);
219
- const compositeWithOutlines = vec4(add(sceneColor.rgb, selectedOutline.add(hoverOutline)), sceneColor.a);
220
- const finalOutput = vec4(mix(bgUniform.current, compositeWithOutlines.rgb, contentAlpha), float(1));
338
+ let compositeWithOutlines = sceneColor;
339
+ if (outlineEnabled) {
340
+ const outlineNode = mergedOutline(scene, camera, {
341
+ primaryObjects: outliner.selectedObjects,
342
+ secondaryObjects: outliner.hoveredObjects,
343
+ primaryEdgeThickness: uniform(1),
344
+ secondaryEdgeThickness: uniform(1.5),
345
+ });
346
+ // Selected: white visible, yellow hidden
347
+ const selectedVisibleColor = uniform(new Color(0xff_ff_ff));
348
+ const selectedHiddenColor = uniform(new Color(0xf3_ff_47));
349
+ const selectedStrength = uniform(3);
350
+ const selectedOutline = outlineNode.primaryVisibleEdge
351
+ .mul(selectedVisibleColor)
352
+ .add(outlineNode.primaryHiddenEdge.mul(selectedHiddenColor))
353
+ .mul(selectedStrength);
354
+ // Hovered: blue visible, yellow hidden, pulsing
355
+ const pulsePeriod = uniform(3);
356
+ const oscillating = oscSine(time.div(pulsePeriod).mul(2)).mul(0.5).add(0.5);
357
+ const osc = mix(oscillating, float(1), hoverPulseMix);
358
+ const hoverOutline = outlineNode.secondaryVisibleEdge
359
+ .mul(hoverVisibleColor)
360
+ .add(outlineNode.secondaryHiddenEdge.mul(hoverHiddenColor))
361
+ .mul(hoverStrength)
362
+ .mul(osc);
363
+ compositeWithOutlines = vec4(add(sceneColor.rgb, selectedOutline.add(hoverOutline)), sceneColor.a);
364
+ }
365
+ const composited = mix(bgUniform.current, compositeWithOutlines.rgb, contentAlpha);
366
+ // Editor overlays painted on top by their own alpha — they never get inked,
367
+ // AO'd, or outlined, and always read crisp regardless of scene depth.
368
+ const withOverlay = mix(composited, overlayColor.rgb, overlayColor.a);
369
+ const finalOutput = vec4(withOverlay, float(1));
221
370
  const renderPipeline = new RenderPipeline(renderer);
222
371
  renderPipeline.outputNode = finalOutput;
223
372
  renderPipelineRef.current = renderPipeline;
224
373
  retryCountRef.current = 0;
225
- console.log('[viewer/post-processing] Pipeline built OK', { version: pipelineVersion });
226
374
  }
227
375
  catch (error) {
228
376
  hasPipelineErrorRef.current = true;
@@ -245,31 +393,49 @@ const PostProcessingPasses = ({ hoverStyles = DEFAULT_HOVER_STYLES, }) => {
245
393
  }, [
246
394
  camera,
247
395
  hoverHiddenColor,
396
+ hoverHighlightMode,
248
397
  hoverPulseMix,
249
398
  hoverStrength,
250
399
  hoverVisibleColor,
400
+ edges,
251
401
  pipelineVersion,
252
402
  projectId,
253
403
  renderer,
254
404
  scene,
405
+ shading,
406
+ size.height,
407
+ size.width,
255
408
  zoneLayers,
409
+ sceneOnlyLayers,
410
+ overlayLayers,
256
411
  ]);
257
412
  useFrame((_, delta) => {
258
- // Animate background colour toward the current theme target (same lerp as AnimatedBackground)
259
- bgTarget.current.set(useViewer.getState().theme === 'dark' ? DARK_BG : LIGHT_BG);
413
+ if (size.width < 1 || size.height < 1) {
414
+ return;
415
+ }
416
+ // Animate background colour toward the current scene theme target (same lerp as AnimatedBackground)
417
+ bgTarget.current.set(getSceneTheme(useViewer.getState().sceneTheme).background);
260
418
  bgCurrent.current.lerp(bgTarget.current, Math.min(delta, 0.1) * 4);
261
419
  bgUniform.current.value.copy(bgCurrent.current);
420
+ // Ink colour follows the (lerping) background luminance — snaps dark↔light.
421
+ inkColorUniform.current.value.set(edgeColorFor(`#${bgCurrent.current.getHexString()}`));
262
422
  const outliner = useViewer.getState().outliner;
263
423
  sanitizeOutlineObjects(outliner.selectedObjects);
264
424
  sanitizeOutlineObjects(outliner.hoveredObjects);
265
- if (hasPipelineErrorRef.current || !renderPipelineRef.current) {
425
+ if (PERF_POST_FX_DISABLED || hasPipelineErrorRef.current || !renderPipelineRef.current) {
266
426
  try {
267
427
  if (renderer.setClearAlpha) {
268
428
  ;
269
429
  renderer.setClearAlpha(1);
270
430
  }
271
- ;
431
+ const submittedAt = PERF_OVERLAY_ENABLED ? performance.now() : 0;
272
432
  renderer.render(scene, camera);
433
+ if (PERF_OVERLAY_ENABLED) {
434
+ const queue = renderer.backend?.device?.queue;
435
+ queue?.onSubmittedWorkDone?.().then(() => {
436
+ pushGpuSample(performance.now() - submittedAt);
437
+ });
438
+ }
273
439
  }
274
440
  catch (fallbackError) {
275
441
  console.error('[viewer/post-processing] Fallback render failed.', fallbackError);
@@ -281,7 +447,19 @@ const PostProcessingPasses = ({ hoverStyles = DEFAULT_HOVER_STYLES, }) => {
281
447
  // making scenePassColor.a a reliable geometry mask (geometry pixels write a=1 via output node).
282
448
  ;
283
449
  renderer.setClearAlpha(0);
450
+ const submittedAt = PERF_OVERLAY_ENABLED ? performance.now() : 0;
284
451
  renderPipelineRef.current.render();
452
+ if (PERF_OVERLAY_ENABLED) {
453
+ // device.queue.onSubmittedWorkDone() resolves once the GPU has
454
+ // finished the work we just submitted — the delta from our submit
455
+ // timestamp is a clean per-frame GPU duration. Doesn't block CPU
456
+ // (no await) and works for the custom RenderPipeline path that
457
+ // bypasses three.js's timestamp-query infrastructure.
458
+ const queue = renderer.backend?.device?.queue;
459
+ queue?.onSubmittedWorkDone?.().then(() => {
460
+ pushGpuSample(performance.now() - submittedAt);
461
+ });
462
+ }
285
463
  }
286
464
  catch (error) {
287
465
  hasPipelineErrorRef.current = true;
@@ -297,7 +475,6 @@ const PostProcessingPasses = ({ hoverStyles = DEFAULT_HOVER_STYLES, }) => {
297
475
  if (retryCountRef.current < MAX_PIPELINE_RETRIES) {
298
476
  // Auto-retry: schedule a pipeline rebuild if we haven't exceeded the retry limit
299
477
  retryCountRef.current++;
300
- console.warn(`[viewer/post-processing] Scheduling pipeline rebuild (attempt ${retryCountRef.current}/${MAX_PIPELINE_RETRIES})`);
301
478
  if (rebuildTimeoutRef.current !== null) {
302
479
  clearTimeout(rebuildTimeoutRef.current);
303
480
  }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Mounts every registered node kind's system component, ordered by
3
+ * `system.priority` (default {@link DEFAULT_PRIORITY}).
4
+ *
5
+ * Today the registry is empty so this component mounts nothing — coexists
6
+ * with legacy `*-System` components in `<Viewer>`. Once kinds register via
7
+ * `@pascal-app/nodes`, each kind's registry-driven system takes over and
8
+ * its legacy counterpart short-circuits via the `nodeRegistry.has(kind)`
9
+ * guard added to each legacy system.
10
+ */
11
+ export declare function RegisteredSystems(): import("react").JSX.Element | null;
12
+ //# sourceMappingURL=registered-systems.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registered-systems.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/registered-systems.tsx"],"names":[],"mappings":"AAoBA;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,uCAsBhC"}
@@ -0,0 +1,47 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { nodeRegistry } from '@pascal-app/core';
4
+ import { lazy, Suspense, useMemo } from 'react';
5
+ const DEFAULT_PRIORITY = 5;
6
+ // Cache lazy components keyed by the module-loader function so React.lazy
7
+ // isn't re-invoked across renders.
8
+ const lazyCache = new WeakMap();
9
+ function loadSystem(def) {
10
+ if (!def.system)
11
+ return null;
12
+ const cached = lazyCache.get(def.system.module);
13
+ if (cached)
14
+ return cached;
15
+ const Comp = lazy(def.system.module);
16
+ lazyCache.set(def.system.module, Comp);
17
+ return Comp;
18
+ }
19
+ /**
20
+ * Mounts every registered node kind's system component, ordered by
21
+ * `system.priority` (default {@link DEFAULT_PRIORITY}).
22
+ *
23
+ * Today the registry is empty so this component mounts nothing — coexists
24
+ * with legacy `*-System` components in `<Viewer>`. Once kinds register via
25
+ * `@pascal-app/nodes`, each kind's registry-driven system takes over and
26
+ * its legacy counterpart short-circuits via the `nodeRegistry.has(kind)`
27
+ * guard added to each legacy system.
28
+ */
29
+ export function RegisteredSystems() {
30
+ const entries = useMemo(() => {
31
+ return Array.from(nodeRegistry.entries())
32
+ .filter(([, def]) => def.system != null)
33
+ .sort(([, a], [, b]) => {
34
+ const pa = a.system?.priority ?? DEFAULT_PRIORITY;
35
+ const pb = b.system?.priority ?? DEFAULT_PRIORITY;
36
+ return pa - pb;
37
+ });
38
+ }, []);
39
+ if (entries.length === 0)
40
+ return null;
41
+ return (_jsx(Suspense, { fallback: null, children: entries.map(([kind, def]) => {
42
+ const Comp = loadSystem(def);
43
+ if (!Comp)
44
+ return null;
45
+ return _jsx(Comp, {}, `registered-system:${kind}`);
46
+ }) }));
47
+ }
@@ -0,0 +1,17 @@
1
+ import { type ReactNode } from 'react';
2
+ import { type Group } from 'three';
3
+ import { type SplitStrategy } from 'three-mesh-bvh';
4
+ type SceneBvhProps = {
5
+ children?: ReactNode;
6
+ enabled?: boolean;
7
+ firstHitOnly?: boolean;
8
+ strategy?: SplitStrategy;
9
+ verbose?: boolean;
10
+ setBoundingBox?: boolean;
11
+ maxDepth?: number;
12
+ maxLeafSize?: number;
13
+ indirect?: boolean;
14
+ };
15
+ export declare const SceneBvh: import("react").ForwardRefExoticComponent<SceneBvhProps & import("react").RefAttributes<Group<import("three").Object3DEventMap>>>;
16
+ export {};
17
+ //# sourceMappingURL=scene-bvh.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scene-bvh.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/scene-bvh.tsx"],"names":[],"mappings":"AACA,OAAO,EAAc,KAAK,SAAS,EAA0C,MAAM,OAAO,CAAA;AAC1F,OAAO,EAAuB,KAAK,KAAK,EAAQ,MAAM,OAAO,CAAA;AAC7D,OAAO,EAKL,KAAK,aAAa,EACnB,MAAM,gBAAgB,CAAA;AAEvB,KAAK,aAAa,GAAG;IACnB,QAAQ,CAAC,EAAE,SAAS,CAAA;IACpB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,QAAQ,CAAC,EAAE,aAAa,CAAA;IACxB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAA;AAeD,eAAO,MAAM,QAAQ,mIAmGpB,CAAA"}
@@ -0,0 +1,94 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useThree } from '@react-three/fiber';
3
+ import { forwardRef, useEffect, useImperativeHandle, useRef } from 'react';
4
+ import { Mesh } from 'three';
5
+ import { acceleratedRaycast, computeBoundsTree, disposeBoundsTree, SAH, } from 'three-mesh-bvh';
6
+ const isMesh = (object) => !!object && typeof object === 'object' && object.isMesh === true;
7
+ const hasBvhCompatibleGeometry = (geometry) => {
8
+ if (!geometry)
9
+ return false;
10
+ const position = geometry.getAttribute('position');
11
+ if (!position)
12
+ return false;
13
+ const vertexCount = geometry.getIndex()?.count ?? position.count;
14
+ return vertexCount >= 3;
15
+ };
16
+ export const SceneBvh = forwardRef(({ children, enabled = true, firstHitOnly = false, strategy = SAH, verbose = false, setBoundingBox = true, maxDepth = 40, maxLeafSize = 10, indirect = false, }, forwardedRef) => {
17
+ const ref = useRef(null);
18
+ const raycaster = useThree((state) => state.raycaster);
19
+ useImperativeHandle(forwardedRef, () => ref.current, []);
20
+ useEffect(() => {
21
+ if (!enabled || !ref.current)
22
+ return;
23
+ const options = {
24
+ strategy,
25
+ verbose,
26
+ setBoundingBox,
27
+ maxDepth,
28
+ maxLeafSize,
29
+ indirect,
30
+ };
31
+ const group = ref.current;
32
+ const acceleratedMeshes = new Set();
33
+ const computedGeometries = new Set();
34
+ raycaster.firstHitOnly = firstHitOnly;
35
+ group.traverse((child) => {
36
+ if (!isMesh(child))
37
+ return;
38
+ if (child.raycast === Mesh.prototype.raycast) {
39
+ child.raycast = acceleratedRaycast;
40
+ acceleratedMeshes.add(child);
41
+ }
42
+ if (child.raycast !== acceleratedRaycast)
43
+ return;
44
+ const geometry = child.geometry;
45
+ if (geometry.boundsTree || !hasBvhCompatibleGeometry(geometry))
46
+ return;
47
+ try {
48
+ // The three-mesh-bvh + @types/three combo doesn't agree on
49
+ // BVH option / class identity (ComputeBVHOptions vs
50
+ // MeshBVHOptions, GeometryBVH vs MeshBVH) — cast through
51
+ // `unknown` to bypass the structural mismatch. Runtime is
52
+ // fine; we're just calling the library's own helpers.
53
+ ;
54
+ geometry.computeBoundsTree =
55
+ computeBoundsTree;
56
+ geometry.disposeBoundsTree =
57
+ disposeBoundsTree;
58
+ geometry.computeBoundsTree(options);
59
+ computedGeometries.add(geometry);
60
+ }
61
+ catch (error) {
62
+ console.warn('[viewer] Skipping BVH for incompatible mesh geometry.', {
63
+ mesh: child.name || child.type,
64
+ error,
65
+ });
66
+ }
67
+ });
68
+ return () => {
69
+ delete raycaster.firstHitOnly;
70
+ for (const geometry of computedGeometries) {
71
+ if (geometry.boundsTree) {
72
+ geometry.disposeBoundsTree();
73
+ }
74
+ }
75
+ for (const mesh of acceleratedMeshes) {
76
+ if (mesh.raycast === acceleratedRaycast) {
77
+ mesh.raycast = Mesh.prototype.raycast;
78
+ }
79
+ }
80
+ };
81
+ }, [
82
+ enabled,
83
+ firstHitOnly,
84
+ strategy,
85
+ verbose,
86
+ setBoundingBox,
87
+ maxDepth,
88
+ maxLeafSize,
89
+ indirect,
90
+ raycaster,
91
+ ]);
92
+ return _jsx("group", { ref: ref, children: children });
93
+ });
94
+ SceneBvh.displayName = 'SceneBvh';
@@ -1,2 +1,2 @@
1
- export declare const SelectionManager: () => import("react/jsx-runtime").JSX.Element;
1
+ export declare const SelectionManager: () => import("react").JSX.Element;
2
2
  //# sourceMappingURL=selection-manager.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"selection-manager.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/selection-manager.tsx"],"names":[],"mappings":"AAoSA,eAAO,MAAM,gBAAgB,+CA4E5B,CAAA"}
1
+ {"version":3,"file":"selection-manager.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/selection-manager.tsx"],"names":[],"mappings":"AA0SA,eAAO,MAAM,gBAAgB,mCA0F5B,CAAA"}