@pascal-app/viewer 1.0.0-beta.5 → 1.0.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 (208) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +67 -0
  3. package/dist/capture/asset-url.d.ts +2 -0
  4. package/dist/capture/asset-url.d.ts.map +1 -0
  5. package/dist/capture/asset-url.js +13 -0
  6. package/dist/capture/capture-runtime.d.ts +43 -0
  7. package/dist/capture/capture-runtime.d.ts.map +1 -0
  8. package/dist/capture/capture-runtime.js +198 -0
  9. package/dist/capture/frame.d.ts +4 -0
  10. package/dist/capture/frame.d.ts.map +1 -0
  11. package/dist/capture/frame.js +21 -0
  12. package/dist/capture/index.d.ts +12 -0
  13. package/dist/capture/index.d.ts.map +1 -0
  14. package/dist/capture/index.js +11 -0
  15. package/dist/capture/layer-visibility.d.ts +5 -0
  16. package/dist/capture/layer-visibility.d.ts.map +1 -0
  17. package/dist/capture/layer-visibility.js +11 -0
  18. package/dist/capture/layers/clay-matcap.d.ts +3 -0
  19. package/dist/capture/layers/clay-matcap.d.ts.map +1 -0
  20. package/dist/capture/layers/clay-matcap.js +26 -0
  21. package/dist/capture/layers/device-motion-layer.d.ts +7 -0
  22. package/dist/capture/layers/device-motion-layer.d.ts.map +1 -0
  23. package/dist/capture/layers/device-motion-layer.js +76 -0
  24. package/dist/capture/layers/point-cloud-layer.d.ts +15 -0
  25. package/dist/capture/layers/point-cloud-layer.d.ts.map +1 -0
  26. package/dist/capture/layers/point-cloud-layer.js +106 -0
  27. package/dist/capture/layers/room-model-layer.d.ts +9 -0
  28. package/dist/capture/layers/room-model-layer.d.ts.map +1 -0
  29. package/dist/capture/layers/room-model-layer.js +71 -0
  30. package/dist/capture/layers/surface-mesh-data.d.ts +9 -0
  31. package/dist/capture/layers/surface-mesh-data.d.ts.map +1 -0
  32. package/dist/capture/layers/surface-mesh-data.js +74 -0
  33. package/dist/capture/layers/surface-mesh-layer.d.ts +7 -0
  34. package/dist/capture/layers/surface-mesh-layer.d.ts.map +1 -0
  35. package/dist/capture/layers/surface-mesh-layer.js +16 -0
  36. package/dist/capture/preview.d.ts +3 -0
  37. package/dist/capture/preview.d.ts.map +1 -0
  38. package/dist/capture/preview.js +2 -0
  39. package/dist/capture/source-state.d.ts +22 -0
  40. package/dist/capture/source-state.d.ts.map +1 -0
  41. package/dist/capture/source-state.js +182 -0
  42. package/dist/capture/stream-rendering.d.ts +14 -0
  43. package/dist/capture/stream-rendering.d.ts.map +1 -0
  44. package/dist/capture/stream-rendering.js +70 -0
  45. package/dist/capture/trajectory.d.ts +20 -0
  46. package/dist/capture/trajectory.d.ts.map +1 -0
  47. package/dist/capture/trajectory.js +77 -0
  48. package/dist/capture/use-json-artifact.d.ts +2 -0
  49. package/dist/capture/use-json-artifact.d.ts.map +1 -0
  50. package/dist/capture/use-json-artifact.js +42 -0
  51. package/dist/components/viewer/batched-mesh-spike.d.ts +14 -0
  52. package/dist/components/viewer/batched-mesh-spike.d.ts.map +1 -0
  53. package/dist/components/viewer/batched-mesh-spike.js +64 -0
  54. package/dist/components/viewer/frame-limiter.d.ts.map +1 -1
  55. package/dist/components/viewer/frame-limiter.js +4 -3
  56. package/dist/components/viewer/glb-scene.d.ts.map +1 -1
  57. package/dist/components/viewer/glb-scene.js +3 -1
  58. package/dist/components/viewer/index.d.ts.map +1 -1
  59. package/dist/components/viewer/index.js +70 -62
  60. package/dist/components/viewer/lights.d.ts.map +1 -1
  61. package/dist/components/viewer/lights.js +123 -89
  62. package/dist/components/viewer/perf-monitor.d.ts +6 -1
  63. package/dist/components/viewer/perf-monitor.d.ts.map +1 -1
  64. package/dist/components/viewer/perf-monitor.js +307 -116
  65. package/dist/components/viewer/perf-panel.d.ts +3 -0
  66. package/dist/components/viewer/perf-panel.d.ts.map +1 -0
  67. package/dist/components/viewer/perf-panel.js +178 -0
  68. package/dist/components/viewer/pointer-raycast-layers.d.ts +15 -0
  69. package/dist/components/viewer/pointer-raycast-layers.d.ts.map +1 -0
  70. package/dist/components/viewer/pointer-raycast-layers.js +28 -0
  71. package/dist/components/viewer/post-processing.d.ts.map +1 -1
  72. package/dist/components/viewer/post-processing.js +130 -83
  73. package/dist/components/viewer/registered-systems.d.ts +14 -5
  74. package/dist/components/viewer/registered-systems.d.ts.map +1 -1
  75. package/dist/components/viewer/registered-systems.js +22 -10
  76. package/dist/components/viewer/scene-atmosphere.d.ts +30 -0
  77. package/dist/components/viewer/scene-atmosphere.d.ts.map +1 -0
  78. package/dist/components/viewer/scene-atmosphere.js +89 -0
  79. package/dist/components/viewer/scene-environment.d.ts.map +1 -1
  80. package/dist/components/viewer/scene-environment.js +19 -5
  81. package/dist/components/viewer/scene-ground-replacement.d.ts +5 -0
  82. package/dist/components/viewer/scene-ground-replacement.d.ts.map +1 -0
  83. package/dist/components/viewer/scene-ground-replacement.js +35 -0
  84. package/dist/components/viewer/selection-manager.d.ts.map +1 -1
  85. package/dist/components/viewer/selection-manager.js +8 -16
  86. package/dist/components/viewer/viewer-camera.d.ts.map +1 -1
  87. package/dist/components/viewer/viewer-camera.js +4 -1
  88. package/dist/components/viewer/viewer-presentations.d.ts +67 -0
  89. package/dist/components/viewer/viewer-presentations.d.ts.map +1 -0
  90. package/dist/components/viewer/viewer-presentations.js +120 -0
  91. package/dist/hooks/use-library-materials-version.d.ts +7 -0
  92. package/dist/hooks/use-library-materials-version.d.ts.map +1 -0
  93. package/dist/hooks/use-library-materials-version.js +10 -0
  94. package/dist/hooks/use-node-events.d.ts.map +1 -1
  95. package/dist/hooks/use-node-events.js +1 -0
  96. package/dist/index.d.ts +16 -7
  97. package/dist/index.d.ts.map +1 -1
  98. package/dist/index.js +16 -7
  99. package/dist/lib/csg-utils.d.ts +1 -0
  100. package/dist/lib/csg-utils.d.ts.map +1 -1
  101. package/dist/lib/csg-utils.js +6 -1
  102. package/dist/lib/gpu-perf.d.ts +0 -2
  103. package/dist/lib/gpu-perf.d.ts.map +1 -1
  104. package/dist/lib/gpu-perf.js +7 -21
  105. package/dist/lib/isolation.d.ts +14 -30
  106. package/dist/lib/isolation.d.ts.map +1 -1
  107. package/dist/lib/isolation.js +62 -66
  108. package/dist/lib/layer-pass.d.ts +28 -0
  109. package/dist/lib/layer-pass.d.ts.map +1 -0
  110. package/dist/lib/layer-pass.js +254 -0
  111. package/dist/lib/materials.d.ts +12 -0
  112. package/dist/lib/materials.d.ts.map +1 -1
  113. package/dist/lib/materials.js +52 -17
  114. package/dist/lib/merged-outline-node.d.ts +17 -8
  115. package/dist/lib/merged-outline-node.d.ts.map +1 -1
  116. package/dist/lib/merged-outline-node.js +194 -52
  117. package/dist/lib/perf-actions.d.ts +55 -0
  118. package/dist/lib/perf-actions.d.ts.map +1 -0
  119. package/dist/lib/perf-actions.js +211 -0
  120. package/dist/lib/perf-observers.d.ts +2 -0
  121. package/dist/lib/perf-observers.d.ts.map +1 -0
  122. package/dist/lib/perf-observers.js +17 -0
  123. package/dist/lib/perf-panel-store.d.ts +71 -0
  124. package/dist/lib/perf-panel-store.d.ts.map +1 -0
  125. package/dist/lib/perf-panel-store.js +29 -0
  126. package/dist/lib/perf-tracks.d.ts +43 -0
  127. package/dist/lib/perf-tracks.d.ts.map +1 -0
  128. package/dist/lib/perf-tracks.js +113 -0
  129. package/dist/lib/pointer-events.d.ts +7 -0
  130. package/dist/lib/pointer-events.d.ts.map +1 -0
  131. package/dist/lib/pointer-events.js +680 -0
  132. package/dist/lib/post-processing-resources.d.ts +12 -0
  133. package/dist/lib/post-processing-resources.d.ts.map +1 -0
  134. package/dist/lib/post-processing-resources.js +19 -0
  135. package/dist/lib/renderer-capability.d.ts.map +1 -1
  136. package/dist/lib/renderer-capability.js +4 -2
  137. package/dist/lib/scene-visibility.d.ts +2 -1
  138. package/dist/lib/scene-visibility.d.ts.map +1 -1
  139. package/dist/lib/scene-visibility.js +25 -2
  140. package/dist/lib/snapshot-pipeline.d.ts +3 -1
  141. package/dist/lib/snapshot-pipeline.d.ts.map +1 -1
  142. package/dist/lib/snapshot-pipeline.js +32 -16
  143. package/dist/store/use-viewer.d.ts +5 -3
  144. package/dist/store/use-viewer.d.ts.map +1 -1
  145. package/dist/store/use-viewer.js +2 -0
  146. package/dist/systems/ceiling/ceiling-system.js +1 -1
  147. package/dist/systems/door/door-animation-system.d.ts.map +1 -1
  148. package/dist/systems/door/door-animation-system.js +6 -9
  149. package/dist/systems/door/door-system.d.ts.map +1 -1
  150. package/dist/systems/door/door-system.js +13 -2
  151. package/dist/systems/floor-elevation/floor-elevation-system.d.ts.map +1 -1
  152. package/dist/systems/floor-elevation/floor-elevation-system.js +7 -3
  153. package/dist/systems/geometry/geometry-system.d.ts.map +1 -1
  154. package/dist/systems/geometry/geometry-system.js +7 -2
  155. package/dist/systems/level/level-system.d.ts.map +1 -1
  156. package/dist/systems/level/level-system.js +1 -1
  157. package/dist/systems/level/level-utils.d.ts +9 -0
  158. package/dist/systems/level/level-utils.d.ts.map +1 -1
  159. package/dist/systems/level/level-utils.js +14 -0
  160. package/dist/systems/perf-action-settle/perf-action-settle-system.d.ts +8 -0
  161. package/dist/systems/perf-action-settle/perf-action-settle-system.d.ts.map +1 -0
  162. package/dist/systems/perf-action-settle/perf-action-settle-system.js +32 -0
  163. package/dist/systems/roof/roof-system.d.ts.map +1 -1
  164. package/dist/systems/roof/roof-system.js +1196 -58
  165. package/dist/systems/scan/scan-system.js +6 -5
  166. package/dist/systems/slab/slab-system.d.ts.map +1 -1
  167. package/dist/systems/slab/slab-system.js +1 -2
  168. package/dist/systems/stair/stair-system.d.ts.map +1 -1
  169. package/dist/systems/stair/stair-system.js +9 -8
  170. package/dist/systems/surface-hole-geometry.d.ts +1 -1
  171. package/dist/systems/surface-hole-geometry.d.ts.map +1 -1
  172. package/dist/systems/surface-hole-geometry.js +1 -1
  173. package/dist/systems/wall/level-miter-cache.d.ts.map +1 -1
  174. package/dist/systems/wall/wall-build-lifecycle.d.ts +9 -0
  175. package/dist/systems/wall/wall-build-lifecycle.d.ts.map +1 -0
  176. package/dist/systems/wall/wall-build-lifecycle.js +74 -0
  177. package/dist/systems/wall/wall-cutout-cache.d.ts +57 -0
  178. package/dist/systems/wall/wall-cutout-cache.d.ts.map +1 -0
  179. package/dist/systems/wall/wall-cutout-cache.js +304 -0
  180. package/dist/systems/wall/wall-cutout.d.ts +12 -1
  181. package/dist/systems/wall/wall-cutout.d.ts.map +1 -1
  182. package/dist/systems/wall/wall-cutout.js +24 -161
  183. package/dist/systems/wall/wall-material-variant.d.ts +31 -0
  184. package/dist/systems/wall/wall-material-variant.d.ts.map +1 -0
  185. package/dist/systems/wall/wall-material-variant.js +10 -0
  186. package/dist/systems/wall/wall-materials.d.ts +6 -0
  187. package/dist/systems/wall/wall-materials.d.ts.map +1 -1
  188. package/dist/systems/wall/wall-materials.js +29 -13
  189. package/dist/systems/wall/wall-placeholder-sweep.d.ts +50 -0
  190. package/dist/systems/wall/wall-placeholder-sweep.d.ts.map +1 -0
  191. package/dist/systems/wall/wall-placeholder-sweep.js +51 -0
  192. package/dist/systems/wall/wall-rebuild-notifications.d.ts +5 -0
  193. package/dist/systems/wall/wall-rebuild-notifications.d.ts.map +1 -0
  194. package/dist/systems/wall/wall-rebuild-notifications.js +23 -0
  195. package/dist/systems/wall/wall-system.d.ts +11 -4
  196. package/dist/systems/wall/wall-system.d.ts.map +1 -1
  197. package/dist/systems/wall/wall-system.js +405 -153
  198. package/dist/systems/window/window-animation-system.d.ts.map +1 -1
  199. package/dist/systems/window/window-animation-system.js +9 -8
  200. package/dist/systems/window/window-system.d.ts +1 -0
  201. package/dist/systems/window/window-system.d.ts.map +1 -1
  202. package/dist/systems/window/window-system.js +16 -2
  203. package/dist/systems/zone/zone-system.d.ts.map +1 -1
  204. package/dist/systems/zone/zone-system.js +28 -2
  205. package/package.json +17 -4
  206. package/dist/lib/polygon-union.d.ts +0 -4
  207. package/dist/lib/polygon-union.d.ts.map +0 -1
  208. package/dist/lib/polygon-union.js +0 -289
package/dist/index.js CHANGED
@@ -17,9 +17,13 @@ export { buildGlbReferenceNodes, buildGlbReplaceNodes, } from './components/view
17
17
  export { GlbScene, } from './components/viewer/glb-scene';
18
18
  export { CROUCH_CAPSULE, CROUCH_EYE_OFFSET, CROUCH_FLOAT_HEIGHT, CROUCH_RUN_SPEED, CROUCH_WALK_SPEED, EYE_LERP_SPEED, GlbWalkthroughController, STAND_CAPSULE, STAND_CLEARANCE, STAND_FLOAT_HEIGHT, WALKTHROUGH_FOV, } from './components/viewer/glb-walkthrough-controller';
19
19
  export { DEFAULT_HOVER_STYLES, SSGI_PARAMS, } from './components/viewer/post-processing';
20
+ export { SceneAtmosphere, useSceneAtmosphere, } from './components/viewer/scene-atmosphere';
20
21
  export { SceneEnvironment } from './components/viewer/scene-environment';
22
+ export { SceneGroundReplacement, useSceneGroundReplacement, } from './components/viewer/scene-ground-replacement';
23
+ export { isViewerPresentationTextureBorrowed, markViewerPresentationTextureBorrowed, registerViewerPresentation, ViewerPresentations, viewerPresentationRegistry, } from './components/viewer/viewer-presentations';
21
24
  export { useAssetUrl } from './hooks/use-asset-url';
22
25
  export { useGLTFKTX2 } from './hooks/use-gltf-ktx2';
26
+ export { useLibraryMaterialsVersion } from './hooks/use-library-materials-version';
23
27
  export { useNodeEvents } from './hooks/use-node-events';
24
28
  export { ASSETS_CDN_URL, resolveAssetUrl, resolveCdnUrl } from './lib/asset-url';
25
29
  export { backdropGradient, deepSkyColor, horizonHazeColor } from './lib/backdrop';
@@ -29,16 +33,21 @@ export { applyWorldScaleBoxUVs } from './lib/box-uv';
29
33
  // because three-bvh-csg / three-mesh-bvh are viewer-only deps.
30
34
  export { ADDITION, Brush, computeGeometryBoundsTree, csgEvaluator, csgGeometry, csgMaterials, INTERSECTION, prepareBrushForCSG, SUBTRACTION, } from './lib/csg-utils';
31
35
  export { disposeObject3DResources } from './lib/dispose-object3d';
36
+ export { PERF_OVERLAY_ENABLED } from './lib/gpu-perf';
32
37
  export { computeHeroFraming, DEFAULT_FRAMING_EXCLUDED_TYPES, heroCameraPose, temporarilyHideNodeTypes, unionRegisteredNodeBounds, } from './lib/hero-pose';
33
- export { applyIsolation, clearIsolation, collectIsolationSubtree, isIsolationActive, } from './lib/isolation';
38
+ export { applyIsolation, clearIsolation, collectIsolationSubtree, isIsolationActive, refreshIsolation, } from './lib/isolation';
34
39
  export { configureKtx2Support, ensureKtx2Support } from './lib/ktx2-loader';
35
- export { BATCHED_LAYER, GRID_LAYER, OVERLAY_LAYER, SCENE_LAYER, setSurfaceRaycastLayers, ZONE_LAYER, } from './lib/layers';
36
- export { applyMaterialPresetToMaterials, BLUEPRINT_PALETTE, baseMaterial, CLAY_PALETTE, clearMaterialCache, createDefaultMaterial, createMaterial, createMaterialFromPresetRef, createSurfaceRoleMaterial, DEFAULT_CEILING_MATERIAL, DEFAULT_DOOR_MATERIAL, DEFAULT_ROOF_MATERIAL, DEFAULT_SHELF_MATERIAL, DEFAULT_SLAB_MATERIAL, DEFAULT_STAIR_MATERIAL, DEFAULT_WALL_MATERIAL, DEFAULT_WINDOW_MATERIAL, disposeMaterial, glassMaterial, MONO_PALETTE, PRESET_PALETTES, resolveMaterialRef, resolveSlotDefaultMaterial, resolveSurfaceColor, WHITE_PALETTE, } from './lib/materials';
40
+ export { LayerPassIndex } from './lib/layer-pass';
41
+ export { BATCHED_LAYER, GRID_LAYER, OVERLAY_LAYER, SCENE_LAYER, SHADOW_ONLY_LAYER, setSurfaceRaycastLayers, ZONE_LAYER, } from './lib/layers';
42
+ export { applyMaterialPresetToMaterials, BLUEPRINT_PALETTE, baseMaterial, CLAY_PALETTE, clearMaterialCache, createDefaultMaterial, createMaterial, createMaterialFromPresetRef, createSurfaceRoleMaterial, DEFAULT_CEILING_MATERIAL, DEFAULT_DOOR_MATERIAL, DEFAULT_ROOF_MATERIAL, DEFAULT_SHELF_MATERIAL, DEFAULT_SLAB_MATERIAL, DEFAULT_STAIR_MATERIAL, DEFAULT_WALL_MATERIAL, DEFAULT_WINDOW_MATERIAL, disposeMaterial, glassMaterial, MONO_PALETTE, PRESET_PALETTES, registerMaterialCacheCleanup, resolveMaterialRef, resolveSlotDefaultMaterial, resolveSurfaceColor, WHITE_PALETTE, } from './lib/materials';
37
43
  export { mergedOutline } from './lib/merged-outline-node';
38
- export { unionPolygons } from './lib/polygon-union';
44
+ export * from './lib/perf-actions';
45
+ export { publishPerfBatchStats } from './lib/perf-panel-store';
46
+ export * from './lib/perf-tracks';
47
+ export { markPureRaycast } from './lib/pointer-events';
39
48
  export { detectRendererCapability, initializeGpuRenderer, } from './lib/renderer-capability';
40
49
  export { getSceneTheme, SCENE_THEME_IDS, SCENE_THEMES, } from './lib/scene-themes';
41
- export { hideFromScene, showInScene } from './lib/scene-visibility';
50
+ export { hideFromScene, showInScene, temporarilyShowShadowOnly, } from './lib/scene-visibility';
42
51
  export { createSnapshotPipeline, SNAPSHOT_MAX_EDGE, SNAPSHOT_MIME, SNAPSHOT_QUALITY, THUMBNAIL_HEIGHT, THUMBNAIL_WIDTH, } from './lib/snapshot-pipeline';
43
52
  export { buildTerrainPerimeterFillGeometry, } from './lib/terrain-perimeter-fill';
44
53
  export { getPascalTextureRef, stampPascalTextureRef, textureMapForSlot, } from './lib/texture-reference';
@@ -67,7 +76,7 @@ export { InteractiveSystem } from './systems/interactive/interactive-system';
67
76
  export { ItemSystem } from './systems/item/item-system';
68
77
  export { ItemLightSystem } from './systems/item-light/item-light-system';
69
78
  export { LevelSystem } from './systems/level/level-system';
70
- export { snapLevelsToTruePositions } from './systems/level/level-utils';
79
+ export { EXPLODED_GAP, getLevelPresentationY, snapLevelsToTruePositions, } from './systems/level/level-utils';
71
80
  export { getRoofMaterialArray } from './systems/roof/roof-materials';
72
81
  // Generic roof-segment primitives. Kinds that compose CSG against
73
82
  // the roof shell (chimney's self-trim, dormer's virtual-segment cut)
@@ -90,7 +99,7 @@ export { getVisibleWallMaterials } from './systems/wall/wall-materials';
90
99
  // definition can compose them into `def.system` without duplicating the
91
100
  // 800+ lines of CSG / mitering logic during Phase 3. These exports are
92
101
  // removed in Phase 6 when the legacy mount points are deleted.
93
- export { drainRebuiltWalls, getPendingWallRebuildCount, WallSystem, } from './systems/wall/wall-system';
102
+ export { drainRebuiltWalls, getPendingWallRebuildCount, isWallInitialBuildActive, WallSystem, } from './systems/wall/wall-system';
94
103
  export { poseWindowMovingParts, WindowAnimationSystem, } from './systems/window/window-animation-system';
95
104
  export { buildWindowPreviewMesh, WindowSystem } from './systems/window/window-system';
96
105
  export { ZoneSystem } from './systems/zone/zone-system';
@@ -6,5 +6,6 @@ export declare function csgMaterials(brush: Brush): THREE.Material[];
6
6
  export declare const csgEvaluator: Evaluator;
7
7
  export declare function computeGeometryBoundsTree(geometry: THREE.BufferGeometry): void;
8
8
  export declare function prepareBrushForCSG(brush: Brush): void;
9
+ export declare function subtractCsgBrush(left: Brush, right: Brush, evaluator: Evaluator): Brush;
9
10
  export { ADDITION, Brush, INTERSECTION, SUBTRACTION } from 'three-bvh-csg';
10
11
  //# sourceMappingURL=csg-utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"csg-utils.d.ts","sourceRoot":"","sources":["../../src/lib/csg-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAE,KAAK,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AA4CrD,wBAAgB,kCAAkC,CAChD,QAAQ,EAAE,KAAK,CAAC,cAAc,GAC7B,KAAK,CAAC,cAAc,CAoCtB;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,CAAC,cAAc,CAE9D;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,CAAC,QAAQ,EAAE,CAG3D;AAED,eAAO,MAAM,YAAY,WAAkB,CAAA;AAK3C,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,KAAK,CAAC,cAAc,QAMvE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,KAAK,QAI9C;AAID,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA"}
1
+ {"version":3,"file":"csg-utils.d.ts","sourceRoot":"","sources":["../../src/lib/csg-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAE,KAAK,KAAK,EAAE,SAAS,EAAe,MAAM,eAAe,CAAA;AA4ClE,wBAAgB,kCAAkC,CAChD,QAAQ,EAAE,KAAK,CAAC,cAAc,GAC7B,KAAK,CAAC,cAAc,CAoCtB;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,CAAC,cAAc,CAE9D;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,CAAC,QAAQ,EAAE,CAG3D;AAED,eAAO,MAAM,YAAY,WAAkB,CAAA;AAK3C,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,KAAK,CAAC,cAAc,QAMvE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,KAAK,QAI9C;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,GAAG,KAAK,CAIvF;AAID,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA"}
@@ -1,5 +1,5 @@
1
1
  import * as THREE from 'three';
2
- import { Evaluator } from 'three-bvh-csg';
2
+ import { Evaluator, SUBTRACTION } from 'three-bvh-csg';
3
3
  import { computeBoundsTree } from 'three-mesh-bvh';
4
4
  /**
5
5
  * Shared CSG primitives used by kinds whose geometry subtracts pieces
@@ -88,6 +88,11 @@ export function prepareBrushForCSG(brush) {
88
88
  computeGeometryBoundsTree(brush.geometry);
89
89
  brush.updateMatrixWorld();
90
90
  }
91
+ export function subtractCsgBrush(left, right, evaluator) {
92
+ const result = evaluator.evaluate(left, right, SUBTRACTION);
93
+ prepareBrushForCSG(result);
94
+ return result;
95
+ }
91
96
  // Re-export Brush + SUBTRACTION + ADDITION + INTERSECTION so kinds don't need a
92
97
  // direct `three-bvh-csg` dependency.
93
98
  export { ADDITION, Brush, INTERSECTION, SUBTRACTION } from 'three-bvh-csg';
@@ -1,4 +1,2 @@
1
1
  export declare const PERF_OVERLAY_ENABLED: boolean;
2
- export declare function pushGpuSample(ms: number): void;
3
- export declare function drainGpuSamples(): number[];
4
2
  //# sourceMappingURL=gpu-perf.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"gpu-perf.d.ts","sourceRoot":"","sources":["../../src/lib/gpu-perf.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,oBAAoB,SACyD,CAAA;AAK1F,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAG9C;AAED,wBAAgB,eAAe,IAAI,MAAM,EAAE,CAK1C"}
1
+ {"version":3,"file":"gpu-perf.d.ts","sourceRoot":"","sources":["../../src/lib/gpu-perf.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,oBAAoB,SACyD,CAAA"}
@@ -1,23 +1,9 @@
1
- // GPU work-time measurement, gated by `?perf` in the URL.
1
+ // `?perf` gate. Kept in its own module because both the overlay and
2
+ // `lib/perf-tracks.ts` (the instrumentation sink every system writes to) read
3
+ // it, and perf-tracks must not import a React component tree.
2
4
  //
3
- // We can't use WebGPU timestamp queries here because the editor renders via
4
- // a custom `RenderPipeline.render()` path that bypasses three.js's built-in
5
- // timestamp infrastructure. Instead we use `device.queue.onSubmittedWorkDone()`,
6
- // which resolves when the GPU finishes all submitted work — measuring the
7
- // CPU→GPU-done delta gives a clean approximation of per-frame GPU duration
8
- // regardless of which render path produced it.
5
+ // Timing itself lives in perf-tracks: `gpu-render` carries three's WebGPU
6
+ // timestamp-query total for the frame's render passes, `gpu-queue` the
7
+ // submit→onSubmittedWorkDone fence, `render-encode` the synchronous CPU cost of
8
+ // building and submitting the frame. See components/viewer/post-processing.tsx.
9
9
  export const PERF_OVERLAY_ENABLED = typeof window !== 'undefined' && new URLSearchParams(window.location.search).has('perf');
10
- const MAX_SAMPLES = 256;
11
- const samples = [];
12
- export function pushGpuSample(ms) {
13
- samples.push(ms);
14
- if (samples.length > MAX_SAMPLES)
15
- samples.shift();
16
- }
17
- export function drainGpuSamples() {
18
- if (samples.length === 0)
19
- return [];
20
- const out = samples.slice();
21
- samples.length = 0;
22
- return out;
23
- }
@@ -1,39 +1,23 @@
1
1
  import type { AnyNodeId } from '@pascal-app/core';
2
2
  import type { Object3D } from 'three';
3
- /** True while an isolation filter is applied (see {@link applyIsolation}). */
3
+ /** True while a subtree is isolated, including before its lazy meshes mount. */
4
4
  export declare function isIsolationActive(): boolean;
5
- /**
6
- * Compute the union of every isolated subtree's `Object3D` descendants.
7
- *
8
- * Pure traversal — exported so future "focus mode" / debug tooling can
9
- * reuse the same definition of "what's in the isolated set". Each root
10
- * is walked via `Object3D.traverse` (the live Three.js graph, not the
11
- * data-model `children` array — those can disagree when systems mount
12
- * synthesized sub-meshes that the data model doesn't track).
13
- */
5
+ /** Include synthesized meshes below a selected native root, including hosted openings. */
14
6
  export declare function collectIsolationSubtree(ids: ReadonlyArray<string>): Set<Object3D>;
15
7
  /**
16
- * Imperative visibility filter on the live `sceneRegistry`. Hides every
17
- * registered group (and its synthesized child meshes) outside the
18
- * isolated subtree by taking it off the scene layer.
19
- *
20
- * Why layers instead of `obj.visible = false`? Three.js's visibility
21
- * flag *cascades* — hiding a parent hides every descendant — so we
22
- * can't hide a host wall while keeping a door rendered inside it.
23
- * Layer masks are per-object and don't cascade: `WebGLRenderer
24
- * .projectObject` skips objects whose layer mask doesn't intersect the
25
- * camera's, but always recurses into their children. So we can hide
26
- * the wall and the door (hosted under it in the scene graph) still
27
- * renders, with its local position relative to the wall preserved
28
- * automatically by the matrix walk.
29
- *
30
- * The mask itself belongs to `lib/scene-visibility.ts`, which reconciles
31
- * isolation with solo's shadow-caster pass, so {@link clearIsolation}
32
- * gives an object back only what isolation took.
33
- *
34
- * Pass `null` to clear isolation (equivalent to calling
35
- * {@link clearIsolation}).
8
+ * Select native subtrees without mutating the saved scene or cascading visibility
9
+ * through their hosts. The next draw also filters unregistered presentation geometry.
36
10
  */
37
11
  export declare function applyIsolation(ids: ReadonlyArray<AnyNodeId> | null): void;
12
+ /**
13
+ * Reconcile immediately before viewport or snapshot rendering. Environment, sky,
14
+ * ground and instance batches can live outside sceneRegistry or mount asynchronously.
15
+ * Lights/cameras remain usable; masks do not cascade, so an isolated door can still
16
+ * render under its hidden wall. Collective renderers fall back to native geometry.
17
+ * The returned cleanup restores fog after the synchronous draw, before atmosphere
18
+ * owners can change it again. Do not hold this cleanup across async GPU readback.
19
+ */
20
+ export declare function refreshIsolation(scene?: Object3D): () => void;
21
+ /** Restore even detached/unregistered meshes, without undoing solo or batching holds. */
38
22
  export declare function clearIsolation(): void;
39
23
  //# sourceMappingURL=isolation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"isolation.d.ts","sourceRoot":"","sources":["../../src/lib/isolation.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAEjD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AASrC,8EAA8E;AAC9E,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,QAAQ,CAAC,CAUjF;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,aAAa,CAAC,SAAS,CAAC,GAAG,IAAI,GAAG,IAAI,CAiBzE;AAUD,wBAAgB,cAAc,IAAI,IAAI,CAUrC"}
1
+ {"version":3,"file":"isolation.d.ts","sourceRoot":"","sources":["../../src/lib/isolation.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAEjD,OAAO,KAAK,EAAE,QAAQ,EAAS,MAAM,OAAO,CAAA;AAQ5C,gFAAgF;AAChF,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAED,0FAA0F;AAC1F,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,QAAQ,CAAC,CASjF;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,aAAa,CAAC,SAAS,CAAC,GAAG,IAAI,GAAG,IAAI,CAOzE;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,CAAC,EAAE,QAAQ,GAAG,MAAM,IAAI,CA6B7D;AAED,yFAAyF;AACzF,wBAAgB,cAAc,IAAI,IAAI,CAKrC"}
@@ -1,91 +1,87 @@
1
1
  'use client';
2
2
  import { sceneRegistry } from '@pascal-app/core';
3
3
  import { hideFromScene, showInScene } from './scene-visibility';
4
- // Whether a subtree is currently isolated (some objects are held off the
5
- // scene layer). Read by consumers that must not act on the partial view — e.g.
6
- // the project-thumbnail autosave skips capturing while isolated so it never
7
- // snapshots a single focused item as the whole project's thumbnail.
8
- let isolationActive = false;
9
- /** True while an isolation filter is applied (see {@link applyIsolation}). */
4
+ let isolatedIds = null;
5
+ let heldObjects = new Set();
6
+ let liveScene;
7
+ const noop = () => { };
8
+ /** True while a subtree is isolated, including before its lazy meshes mount. */
10
9
  export function isIsolationActive() {
11
- return isolationActive;
10
+ return isolatedIds !== null;
12
11
  }
13
- /**
14
- * Compute the union of every isolated subtree's `Object3D` descendants.
15
- *
16
- * Pure traversal — exported so future "focus mode" / debug tooling can
17
- * reuse the same definition of "what's in the isolated set". Each root
18
- * is walked via `Object3D.traverse` (the live Three.js graph, not the
19
- * data-model `children` array — those can disagree when systems mount
20
- * synthesized sub-meshes that the data model doesn't track).
21
- */
12
+ /** Include synthesized meshes below a selected native root, including hosted openings. */
22
13
  export function collectIsolationSubtree(ids) {
23
14
  const keep = new Set();
24
15
  for (const id of ids) {
25
16
  const root = sceneRegistry.nodes.get(id);
26
- if (!root)
27
- continue;
28
- root.traverse((child) => {
17
+ root?.traverse((child) => {
29
18
  keep.add(child);
30
19
  });
31
20
  }
32
21
  return keep;
33
22
  }
34
23
  /**
35
- * Imperative visibility filter on the live `sceneRegistry`. Hides every
36
- * registered group (and its synthesized child meshes) outside the
37
- * isolated subtree by taking it off the scene layer.
38
- *
39
- * Why layers instead of `obj.visible = false`? Three.js's visibility
40
- * flag *cascades* — hiding a parent hides every descendant — so we
41
- * can't hide a host wall while keeping a door rendered inside it.
42
- * Layer masks are per-object and don't cascade: `WebGLRenderer
43
- * .projectObject` skips objects whose layer mask doesn't intersect the
44
- * camera's, but always recurses into their children. So we can hide
45
- * the wall and the door (hosted under it in the scene graph) still
46
- * renders, with its local position relative to the wall preserved
47
- * automatically by the matrix walk.
48
- *
49
- * The mask itself belongs to `lib/scene-visibility.ts`, which reconciles
50
- * isolation with solo's shadow-caster pass, so {@link clearIsolation}
51
- * gives an object back only what isolation took.
52
- *
53
- * Pass `null` to clear isolation (equivalent to calling
54
- * {@link clearIsolation}).
24
+ * Select native subtrees without mutating the saved scene or cascading visibility
25
+ * through their hosts. The next draw also filters unregistered presentation geometry.
55
26
  */
56
27
  export function applyIsolation(ids) {
57
28
  if (ids == null || ids.length === 0) {
58
29
  clearIsolation();
59
30
  return;
60
31
  }
61
- const keep = collectIsolationSubtree(ids);
62
- // Iterate registered roots. For each one outside the keep set,
63
- // hide it and every descendant — *except* descendants that are
64
- // themselves in `keep` (a kept node nested under a non-kept host:
65
- // the isolated door under the hidden wall).
66
- for (const [, obj] of sceneRegistry.nodes) {
67
- if (keep.has(obj))
68
- continue;
69
- hideRecursive(obj, keep);
70
- }
71
- isolationActive = true;
32
+ isolatedIds = [...ids];
33
+ refreshIsolation();
72
34
  }
73
- function hideRecursive(obj, keep) {
74
- if (keep.has(obj))
75
- return;
76
- hideFromScene(obj, 'isolated');
77
- for (const child of obj.children) {
78
- hideRecursive(child, keep);
79
- }
35
+ /**
36
+ * Reconcile immediately before viewport or snapshot rendering. Environment, sky,
37
+ * ground and instance batches can live outside sceneRegistry or mount asynchronously.
38
+ * Lights/cameras remain usable; masks do not cascade, so an isolated door can still
39
+ * render under its hidden wall. Collective renderers fall back to native geometry.
40
+ * The returned cleanup restores fog after the synchronous draw, before atmosphere
41
+ * owners can change it again. Do not hold this cleanup across async GPU readback.
42
+ */
43
+ export function refreshIsolation(scene) {
44
+ if (!isolatedIds)
45
+ return noop;
46
+ if (scene)
47
+ liveScene = scene;
48
+ const keep = collectIsolationSubtree(isolatedIds);
49
+ const visited = new Set();
50
+ const hidden = new Set();
51
+ const visit = (object) => {
52
+ if (visited.has(object))
53
+ return;
54
+ visited.add(object);
55
+ const flags = object;
56
+ if (keep.has(object) || flags.isLight || flags.isCamera)
57
+ return;
58
+ hideFromScene(object, 'isolated');
59
+ hidden.add(object);
60
+ };
61
+ liveScene?.traverse(visit);
62
+ // Also covers not-yet-attached registered roots and imperative callers without a scene.
63
+ for (const object of sceneRegistry.nodes.values())
64
+ object.traverse(visit);
65
+ for (const object of heldObjects)
66
+ if (!hidden.has(object))
67
+ showInScene(object, 'isolated');
68
+ heldObjects = hidden;
69
+ const atmosphere = scene;
70
+ if (!atmosphere?.isScene)
71
+ return noop;
72
+ const { fog, fogNode } = atmosphere;
73
+ atmosphere.fog = null;
74
+ atmosphere.fogNode = null;
75
+ return () => {
76
+ atmosphere.fog = fog;
77
+ atmosphere.fogNode = fogNode;
78
+ };
80
79
  }
80
+ /** Restore even detached/unregistered meshes, without undoing solo or batching holds. */
81
81
  export function clearIsolation() {
82
- // We don't know which objects were touched without re-walking, so
83
- // walk every registered root + its descendants and drop the isolation
84
- // reason wherever it was set. `traverse` is cheap and idempotent here.
85
- for (const [, obj] of sceneRegistry.nodes) {
86
- obj.traverse((child) => {
87
- showInScene(child, 'isolated');
88
- });
89
- }
90
- isolationActive = false;
82
+ for (const object of heldObjects)
83
+ showInScene(object, 'isolated');
84
+ heldObjects.clear();
85
+ isolatedIds = null;
86
+ liveScene = undefined;
91
87
  }
@@ -0,0 +1,28 @@
1
+ import { type Camera, Object3D, type Scene } from 'three';
2
+ import { type NodeFrame, PassNode } from 'three/webgpu';
3
+ export declare class LayerPassIndex {
4
+ readonly source: Scene;
5
+ private readonly members;
6
+ private readonly cleanups;
7
+ constructor(source: Scene, layers: number[]);
8
+ register(subtree: Object3D): void;
9
+ private attach;
10
+ private detach;
11
+ prepare(layer: number, roots: Object3D[]): {
12
+ drawable: boolean;
13
+ shadowLight: boolean;
14
+ };
15
+ dispose(): void;
16
+ }
17
+ export declare class LayerPassNode extends PassNode {
18
+ private readonly index;
19
+ private readonly layer;
20
+ private readonly mainPass;
21
+ private readonly roots;
22
+ private readonly size;
23
+ private needsClear;
24
+ constructor(index: LayerPassIndex, camera: Camera, layer: number, mainPass: PassNode);
25
+ dispose(): void;
26
+ updateBefore(frame: NodeFrame): undefined;
27
+ }
28
+ //# sourceMappingURL=layer-pass.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layer-pass.d.ts","sourceRoot":"","sources":["../../src/lib/layer-pass.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,MAAM,EAAiB,QAAQ,EAAE,KAAK,KAAK,EAAW,MAAM,OAAO,CAAA;AACjF,OAAO,EAAE,KAAK,SAAS,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAoBvD,qBAAa,cAAc;IAKvB,QAAQ,CAAC,MAAM,EAAE,KAAK;IAJxB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmC;IAC3D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkC;gBAGhD,MAAM,EAAE,KAAK,EACtB,MAAM,EAAE,MAAM,EAAE;IAWlB,QAAQ,CAAC,OAAO,EAAE,QAAQ;IAU1B,OAAO,CAAC,MAAM,CA2Cb;IAED,OAAO,CAAC,MAAM;IAMd,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE;;;;IAoCxC,OAAO;CAIR;AAiBD,qBAAa,aAAc,SAAQ,QAAQ;IAMvC,OAAO,CAAC,QAAQ,CAAC,KAAK;IAEtB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAR3B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiB;IACvC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAgB;IACrC,OAAO,CAAC,UAAU,CAAO;gBAGN,KAAK,EAAE,cAAc,EACtC,MAAM,EAAE,MAAM,EACG,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,QAAQ;IAc5B,OAAO;IAKP,YAAY,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS;CAgEnD"}
@@ -0,0 +1,254 @@
1
+ // Only one index may observe a scene, and observed objects must own their Layers.
2
+ // Insertions without childadded are not observed: call register(subtree) afterward.
3
+ // Replaced Layers are repaired for known members during prepare(); other objects
4
+ // need register(object), since discovering them would require a full-scene scan.
5
+ import { Object3D, Vector2 } from 'three';
6
+ import { PassNode } from 'three/webgpu';
7
+ const maskObserver = Symbol('LayerPassIndex.maskObserver');
8
+ function maskOwner(object) {
9
+ const get = Object.getOwnPropertyDescriptor(object.layers, 'mask')?.get;
10
+ return get?.[maskObserver];
11
+ }
12
+ // Observe mask writes as well as child events: R3F's numeric `layers` prop calls
13
+ // Layers.set(), while imperative producers also use enable() and direct masks.
14
+ // Nothing on Object3D/Layers.prototype is patched, and detach restores the field.
15
+ export class LayerPassIndex {
16
+ source;
17
+ members = new Map();
18
+ cleanups = new Map();
19
+ constructor(source, layers) {
20
+ this.source = source;
21
+ for (const layer of layers)
22
+ this.members.set(1 << layer, new Set());
23
+ try {
24
+ this.attach(source);
25
+ }
26
+ catch (error) {
27
+ this.dispose();
28
+ throw error;
29
+ }
30
+ }
31
+ register(subtree) {
32
+ this.detach(subtree);
33
+ for (let object = subtree; object; object = object.parent) {
34
+ if (object === this.source) {
35
+ this.attach(subtree);
36
+ return;
37
+ }
38
+ }
39
+ }
40
+ attach = (object) => {
41
+ if (this.cleanups.has(object))
42
+ return;
43
+ const layers = object.layers;
44
+ if (maskOwner(object)) {
45
+ throw new Error('LayerPassIndex requires one index per scene and unshared Layers');
46
+ }
47
+ let mask = layers.mask;
48
+ const sync = () => {
49
+ for (const [bit, members] of this.members) {
50
+ if ((mask & bit) !== 0 && object !== this.source)
51
+ members.add(object);
52
+ else
53
+ members.delete(object);
54
+ }
55
+ };
56
+ const getMask = Object.assign(() => mask, { [maskObserver]: object });
57
+ Object.defineProperty(layers, 'mask', {
58
+ configurable: true,
59
+ enumerable: true,
60
+ get: getMask,
61
+ set: (value) => {
62
+ if (mask === value)
63
+ return;
64
+ mask = value;
65
+ sync();
66
+ },
67
+ });
68
+ const added = ({ child }) => this.attach(child);
69
+ const removed = ({ child }) => this.detach(child);
70
+ object.addEventListener('childadded', added);
71
+ object.addEventListener('childremoved', removed);
72
+ this.cleanups.set(object, () => {
73
+ object.removeEventListener('childadded', added);
74
+ object.removeEventListener('childremoved', removed);
75
+ if (Object.getOwnPropertyDescriptor(layers, 'mask')?.get === getMask) {
76
+ Object.defineProperty(layers, 'mask', {
77
+ configurable: true,
78
+ enumerable: true,
79
+ writable: true,
80
+ value: mask,
81
+ });
82
+ }
83
+ for (const members of this.members.values())
84
+ members.delete(object);
85
+ });
86
+ sync();
87
+ for (const child of object.children)
88
+ this.attach(child);
89
+ };
90
+ detach(object) {
91
+ for (const child of object.children)
92
+ this.detach(child);
93
+ this.cleanups.get(object)?.();
94
+ this.cleanups.delete(object);
95
+ }
96
+ prepare(layer, roots) {
97
+ roots.length = 0;
98
+ const members = this.members.get(1 << layer);
99
+ // Repairs can change membership, so finish them before choosing nested roots.
100
+ for (const tracked of this.members.values()) {
101
+ for (const object of [...tracked]) {
102
+ let root = object;
103
+ while (root.parent && root !== this.source)
104
+ root = root.parent;
105
+ if (root !== this.source)
106
+ this.detach(root);
107
+ else if (maskOwner(object) !== object)
108
+ this.register(object);
109
+ }
110
+ }
111
+ let drawable = false;
112
+ let shadowLight = false;
113
+ for (const object of members) {
114
+ let visible = object.visible;
115
+ let nested = false;
116
+ for (let parent = object.parent; parent; parent = parent.parent) {
117
+ if (!parent.visible)
118
+ visible = false;
119
+ if (members.has(parent))
120
+ nested = true;
121
+ }
122
+ if (!visible)
123
+ continue;
124
+ const { material, isLight, castShadow } = object;
125
+ if (material) {
126
+ drawable ||= Array.isArray(material) ? material.some((m) => m.visible) : material.visible;
127
+ }
128
+ shadowLight ||= isLight === true && castShadow;
129
+ if (!nested)
130
+ roots.push(object);
131
+ }
132
+ // Keep source traversal order even when a producer switches layers after
133
+ // mounting. Ancestors on this layer remain intact, retaining group order,
134
+ // clipping and LOD behavior; layers do not inherit through other ancestors.
135
+ roots.sort(compareSceneOrder);
136
+ return { drawable, shadowLight };
137
+ }
138
+ dispose() {
139
+ for (const cleanup of this.cleanups.values())
140
+ cleanup();
141
+ this.cleanups.clear();
142
+ }
143
+ }
144
+ function compareSceneOrder(a, b) {
145
+ const pathA = [];
146
+ const pathB = [];
147
+ for (let object = a; object; object = object.parent)
148
+ pathA.push(object);
149
+ for (let object = b; object; object = object.parent)
150
+ pathB.push(object);
151
+ let i = pathA.length - 1;
152
+ let j = pathB.length - 1;
153
+ while (i >= 0 && j >= 0 && pathA[i] === pathB[j]) {
154
+ i--;
155
+ j--;
156
+ }
157
+ const siblings = pathA[i + 1].children;
158
+ return siblings.indexOf(pathA[i]) - siblings.indexOf(pathB[j]);
159
+ }
160
+ export class LayerPassNode extends PassNode {
161
+ index;
162
+ layer;
163
+ mainPass;
164
+ roots = [];
165
+ size = new Vector2();
166
+ needsClear = true;
167
+ constructor(index, camera, layer, mainPass) {
168
+ super(PassNode.COLOR, index.source, camera);
169
+ this.index = index;
170
+ this.layer = layer;
171
+ this.mainPass = mainPass;
172
+ // A scene view, not cloned meshes: callbacks, environment, fog, materials,
173
+ // skeletons and world matrices retain their original owners and values.
174
+ this.scene = new Proxy(index.source, {
175
+ get: (source, key) => {
176
+ if (key === 'children')
177
+ return this.roots;
178
+ if (key === 'matrixWorldAutoUpdate')
179
+ return false;
180
+ return Reflect.get(source, key, source);
181
+ },
182
+ });
183
+ }
184
+ dispose() {
185
+ this.roots.length = 0;
186
+ super.dispose();
187
+ }
188
+ updateBefore(frame) {
189
+ // NodeFrame deduplicates FRAME updates. Make the dependency explicit rather
190
+ // than relying on which composite expression the TSL builder visits first.
191
+ frame.updateBeforeNode(this.mainPass);
192
+ const { drawable, shadowLight } = this.index.prepare(this.layer, this.roots);
193
+ const renderer = frame.renderer;
194
+ const source = this.index.source;
195
+ const hasBackground = source.background !== null || ('backgroundNode' in source && source.backgroundNode != null);
196
+ const hasSceneCallbacks = source.onBeforeRender !== Object3D.prototype.onBeforeRender ||
197
+ source.onAfterRender !== Object3D.prototype.onAfterRender;
198
+ if (drawable || hasBackground || hasSceneCallbacks) {
199
+ this.needsClear = true;
200
+ // Shadows need all source casters; custom callbacks need the original
201
+ // scene receiver and graph, including when they enable an empty layer.
202
+ const root = this.scene;
203
+ if (shadowLight || hasSceneCallbacks)
204
+ this.scene = source;
205
+ try {
206
+ if (this.scene !== source) {
207
+ const updatedAncestors = new Set();
208
+ const updateAncestor = (object) => {
209
+ if (!object || updatedAncestors.has(object))
210
+ return;
211
+ updateAncestor(object.parent);
212
+ // Force world refreshes even when a manual local matrix is clean.
213
+ object.matrixWorldNeedsUpdate = true;
214
+ object.updateWorldMatrix(false, false);
215
+ updatedAncestors.add(object);
216
+ };
217
+ for (const root of this.roots) {
218
+ updateAncestor(root.parent);
219
+ root.updateMatrixWorld(true);
220
+ }
221
+ }
222
+ super.updateBefore(frame);
223
+ }
224
+ finally {
225
+ this.scene = root;
226
+ }
227
+ return;
228
+ }
229
+ const outputTarget = renderer.getOutputRenderTarget();
230
+ if (outputTarget && 'isXRRenderTarget' in outputTarget && outputTarget.isXRRenderTarget)
231
+ this.size.set(outputTarget.width, outputTarget.height);
232
+ else
233
+ renderer.getDrawingBufferSize(this.size);
234
+ const width = this.renderTarget.width;
235
+ const height = this.renderTarget.height;
236
+ this.setSize(this.size.x, this.size.y);
237
+ if (width !== this.renderTarget.width || height !== this.renderTarget.height)
238
+ this.needsClear = true;
239
+ if (!this.needsClear)
240
+ return;
241
+ const target = renderer.getRenderTarget();
242
+ const mrt = renderer.getMRT();
243
+ try {
244
+ renderer.setRenderTarget(this.renderTarget);
245
+ renderer.setMRT(null);
246
+ renderer.clear(true, true, true);
247
+ this.needsClear = false;
248
+ }
249
+ finally {
250
+ renderer.setRenderTarget(target);
251
+ renderer.setMRT(mrt);
252
+ }
253
+ }
254
+ }