@ifc-lite/renderer 3.2.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/aabb-edges.d.ts +10 -0
- package/dist/aabb-edges.d.ts.map +1 -1
- package/dist/aabb-edges.js +13 -0
- package/dist/aabb-edges.js.map +1 -1
- package/dist/ao-params.d.ts +85 -0
- package/dist/ao-params.d.ts.map +1 -0
- package/dist/ao-params.js +109 -0
- package/dist/ao-params.js.map +1 -0
- package/dist/ao-pass.d.ts +47 -0
- package/dist/ao-pass.d.ts.map +1 -0
- package/dist/ao-pass.js +157 -0
- package/dist/ao-pass.js.map +1 -0
- package/dist/camera-matrices.d.ts +3 -0
- package/dist/camera-matrices.d.ts.map +1 -1
- package/dist/camera-matrices.js +12 -1
- package/dist/camera-matrices.js.map +1 -1
- package/dist/camera-orientation.d.ts +62 -0
- package/dist/camera-orientation.d.ts.map +1 -0
- package/dist/camera-orientation.js +155 -0
- package/dist/camera-orientation.js.map +1 -0
- package/dist/camera-projection.d.ts +3 -1
- package/dist/camera-projection.d.ts.map +1 -1
- package/dist/camera-projection.js +26 -20
- package/dist/camera-projection.js.map +1 -1
- package/dist/camera-surface-zoom.d.ts +32 -0
- package/dist/camera-surface-zoom.d.ts.map +1 -0
- package/dist/camera-surface-zoom.js +37 -0
- package/dist/camera-surface-zoom.js.map +1 -0
- package/dist/camera.d.ts +32 -61
- package/dist/camera.d.ts.map +1 -1
- package/dist/camera.js +71 -167
- package/dist/camera.js.map +1 -1
- package/dist/clash-solid-pipeline.d.ts +9 -3
- package/dist/clash-solid-pipeline.d.ts.map +1 -1
- package/dist/clash-solid-pipeline.js +31 -8
- package/dist/clash-solid-pipeline.js.map +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +4 -3
- package/dist/constants.js.map +1 -1
- package/dist/depth-reconstruct.d.ts +43 -0
- package/dist/depth-reconstruct.d.ts.map +1 -0
- package/dist/depth-reconstruct.js +35 -0
- package/dist/depth-reconstruct.js.map +1 -0
- package/dist/device.js +1 -1
- package/dist/device.js.map +1 -1
- package/dist/edge-params.d.ts +43 -0
- package/dist/edge-params.d.ts.map +1 -0
- package/dist/edge-params.js +63 -0
- package/dist/edge-params.js.map +1 -0
- package/dist/edge-pass.d.ts +38 -0
- package/dist/edge-pass.d.ts.map +1 -0
- package/dist/edge-pass.js +111 -0
- package/dist/edge-pass.js.map +1 -0
- package/dist/environment.d.ts +26 -15
- package/dist/environment.d.ts.map +1 -1
- package/dist/environment.js +24 -13
- package/dist/environment.js.map +1 -1
- package/dist/federation-registry.d.ts +16 -0
- package/dist/federation-registry.d.ts.map +1 -1
- package/dist/federation-registry.js +80 -9
- package/dist/federation-registry.js.map +1 -1
- package/dist/gpu-static-upload.d.ts +39 -0
- package/dist/gpu-static-upload.d.ts.map +1 -0
- package/dist/gpu-static-upload.js +70 -0
- package/dist/gpu-static-upload.js.map +1 -0
- package/dist/gpu-upload-guard.d.ts +12 -23
- package/dist/gpu-upload-guard.d.ts.map +1 -1
- package/dist/gpu-upload-guard.js +12 -26
- package/dist/gpu-upload-guard.js.map +1 -1
- package/dist/index.d.ts +58 -35
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +255 -284
- package/dist/index.js.map +1 -1
- package/dist/instanced-render.d.ts +25 -5
- package/dist/instanced-render.d.ts.map +1 -1
- package/dist/instanced-render.js +50 -13
- package/dist/instanced-render.js.map +1 -1
- package/dist/instanced-rte.d.ts +17 -0
- package/dist/instanced-rte.d.ts.map +1 -0
- package/dist/instanced-rte.js +32 -0
- package/dist/instanced-rte.js.map +1 -0
- package/dist/mesh-material.d.ts +26 -0
- package/dist/mesh-material.d.ts.map +1 -0
- package/dist/mesh-material.js +52 -0
- package/dist/mesh-material.js.map +1 -0
- package/dist/mesh-rte-uniforms.d.ts +47 -0
- package/dist/mesh-rte-uniforms.d.ts.map +1 -0
- package/dist/mesh-rte-uniforms.js +60 -0
- package/dist/mesh-rte-uniforms.js.map +1 -0
- package/dist/model-bounds-tracker.d.ts +7 -0
- package/dist/model-bounds-tracker.d.ts.map +1 -1
- package/dist/model-bounds-tracker.js +16 -5
- package/dist/model-bounds-tracker.js.map +1 -1
- package/dist/model-translation.d.ts +1 -1
- package/dist/model-translation.d.ts.map +1 -1
- package/dist/model-translation.js +50 -2
- package/dist/model-translation.js.map +1 -1
- package/dist/overlay-theme-uniforms.d.ts +54 -0
- package/dist/overlay-theme-uniforms.d.ts.map +1 -0
- package/dist/overlay-theme-uniforms.js +89 -0
- package/dist/overlay-theme-uniforms.js.map +1 -0
- package/dist/overlay-theme.d.ts +73 -0
- package/dist/overlay-theme.d.ts.map +1 -0
- package/dist/overlay-theme.js +24 -0
- package/dist/overlay-theme.js.map +1 -0
- package/dist/pick-rte-snapshot.d.ts +7 -0
- package/dist/pick-rte-snapshot.d.ts.map +1 -0
- package/dist/pick-rte-snapshot.js +15 -0
- package/dist/pick-rte-snapshot.js.map +1 -0
- package/dist/pick-types.d.ts +17 -0
- package/dist/pick-types.d.ts.map +1 -0
- package/dist/pick-types.js +5 -0
- package/dist/pick-types.js.map +1 -0
- package/dist/pick-uniforms.d.ts +6 -0
- package/dist/pick-uniforms.d.ts.map +1 -1
- package/dist/pick-uniforms.js +22 -18
- package/dist/pick-uniforms.js.map +1 -1
- package/dist/pick-world-position.d.ts +18 -0
- package/dist/pick-world-position.d.ts.map +1 -0
- package/dist/pick-world-position.js +21 -0
- package/dist/pick-world-position.js.map +1 -0
- package/dist/picker-readbacks.d.ts +6 -0
- package/dist/picker-readbacks.d.ts.map +1 -0
- package/dist/picker-readbacks.js +20 -0
- package/dist/picker-readbacks.js.map +1 -0
- package/dist/picker-rte-uniforms.d.ts +6 -0
- package/dist/picker-rte-uniforms.d.ts.map +1 -0
- package/dist/picker-rte-uniforms.js +41 -0
- package/dist/picker-rte-uniforms.js.map +1 -0
- package/dist/picker.d.ts +6 -7
- package/dist/picker.d.ts.map +1 -1
- package/dist/picker.js +88 -115
- package/dist/picker.js.map +1 -1
- package/dist/picking-manager.d.ts +8 -0
- package/dist/picking-manager.d.ts.map +1 -1
- package/dist/picking-manager.js +46 -19
- package/dist/picking-manager.js.map +1 -1
- package/dist/pipeline.d.ts +3 -7
- package/dist/pipeline.d.ts.map +1 -1
- package/dist/pipeline.js +27 -31
- package/dist/pipeline.js.map +1 -1
- package/dist/point-picker.d.ts +5 -1
- package/dist/point-picker.d.ts.map +1 -1
- package/dist/point-picker.js +49 -9
- package/dist/point-picker.js.map +1 -1
- package/dist/pointcloud/point-cloud-handle-ids.d.ts +22 -0
- package/dist/pointcloud/point-cloud-handle-ids.d.ts.map +1 -0
- package/dist/pointcloud/point-cloud-handle-ids.js +31 -0
- package/dist/pointcloud/point-cloud-handle-ids.js.map +1 -0
- package/dist/pointcloud/point-cloud-node.d.ts +14 -6
- package/dist/pointcloud/point-cloud-node.d.ts.map +1 -1
- package/dist/pointcloud/point-cloud-node.js.map +1 -1
- package/dist/pointcloud/point-cloud-pick-sources.d.ts +47 -0
- package/dist/pointcloud/point-cloud-pick-sources.d.ts.map +1 -0
- package/dist/pointcloud/point-cloud-pick-sources.js +32 -0
- package/dist/pointcloud/point-cloud-pick-sources.js.map +1 -0
- package/dist/pointcloud/point-cloud-placement.d.ts.map +1 -1
- package/dist/pointcloud/point-cloud-placement.js +20 -5
- package/dist/pointcloud/point-cloud-placement.js.map +1 -1
- package/dist/pointcloud/point-cloud-ray-transform.d.ts +78 -4
- package/dist/pointcloud/point-cloud-ray-transform.d.ts.map +1 -1
- package/dist/pointcloud/point-cloud-ray-transform.js +31 -84
- package/dist/pointcloud/point-cloud-ray-transform.js.map +1 -1
- package/dist/pointcloud/point-cloud-renderer.d.ts +21 -19
- package/dist/pointcloud/point-cloud-renderer.d.ts.map +1 -1
- package/dist/pointcloud/point-cloud-renderer.js +46 -52
- package/dist/pointcloud/point-cloud-renderer.js.map +1 -1
- package/dist/pointcloud/point-cloud-stream-lifecycle.d.ts +44 -0
- package/dist/pointcloud/point-cloud-stream-lifecycle.d.ts.map +1 -0
- package/dist/pointcloud/point-cloud-stream-lifecycle.js +44 -0
- package/dist/pointcloud/point-cloud-stream-lifecycle.js.map +1 -0
- package/dist/pointcloud/point-cloud-uniforms.d.ts +5 -0
- package/dist/pointcloud/point-cloud-uniforms.d.ts.map +1 -1
- package/dist/pointcloud/point-cloud-uniforms.js +55 -44
- package/dist/pointcloud/point-cloud-uniforms.js.map +1 -1
- package/dist/pointcloud/point-cloud-visibility.d.ts +10 -0
- package/dist/pointcloud/point-cloud-visibility.d.ts.map +1 -0
- package/dist/pointcloud/point-cloud-visibility.js +18 -0
- package/dist/pointcloud/point-cloud-visibility.js.map +1 -0
- package/dist/pointcloud/point-pipeline.d.ts +4 -9
- package/dist/pointcloud/point-pipeline.d.ts.map +1 -1
- package/dist/pointcloud/point-pipeline.js +4 -9
- package/dist/pointcloud/point-pipeline.js.map +1 -1
- package/dist/pointcloud/point-shader.wgsl.d.ts +1 -21
- package/dist/pointcloud/point-shader.wgsl.d.ts.map +1 -1
- package/dist/pointcloud/point-shader.wgsl.js +25 -5
- package/dist/pointcloud/point-shader.wgsl.js.map +1 -1
- package/dist/post-pass-chain.d.ts +38 -0
- package/dist/post-pass-chain.d.ts.map +1 -0
- package/dist/post-pass-chain.js +105 -0
- package/dist/post-pass-chain.js.map +1 -0
- package/dist/raycast-engine.d.ts +7 -5
- package/dist/raycast-engine.d.ts.map +1 -1
- package/dist/raycast-engine.js +17 -20
- package/dist/raycast-engine.js.map +1 -1
- package/dist/raycast-point-cloud-query.d.ts +1 -1
- package/dist/raycast-point-cloud-query.d.ts.map +1 -1
- package/dist/reference-image-pipeline.d.ts +1 -1
- package/dist/reference-image-pipeline.d.ts.map +1 -1
- package/dist/reference-image-pipeline.js +25 -7
- package/dist/reference-image-pipeline.js.map +1 -1
- package/dist/reference-images.d.ts +1 -1
- package/dist/reference-images.d.ts.map +1 -1
- package/dist/reference-images.js +13 -5
- package/dist/reference-images.js.map +1 -1
- package/dist/relative-to-eye.d.ts +146 -0
- package/dist/relative-to-eye.d.ts.map +1 -0
- package/dist/relative-to-eye.js +326 -0
- package/dist/relative-to-eye.js.map +1 -0
- package/dist/render-section-draw.d.ts +11 -0
- package/dist/render-section-draw.d.ts.map +1 -1
- package/dist/render-section-draw.js +6 -3
- package/dist/render-section-draw.js.map +1 -1
- package/dist/renderer-color-readback.d.ts +50 -0
- package/dist/renderer-color-readback.d.ts.map +1 -0
- package/dist/renderer-color-readback.js +145 -0
- package/dist/renderer-color-readback.js.map +1 -0
- package/dist/renderer-overlays.d.ts +20 -10
- package/dist/renderer-overlays.d.ts.map +1 -1
- package/dist/renderer-overlays.js +44 -34
- package/dist/renderer-overlays.js.map +1 -1
- package/dist/renderer-screenshot.d.ts +4 -0
- package/dist/renderer-screenshot.d.ts.map +1 -0
- package/dist/renderer-screenshot.js +17 -0
- package/dist/renderer-screenshot.js.map +1 -0
- package/dist/renderer-symbolic-overlays.d.ts +4 -2
- package/dist/renderer-symbolic-overlays.d.ts.map +1 -1
- package/dist/renderer-symbolic-overlays.js +33 -13
- package/dist/renderer-symbolic-overlays.js.map +1 -1
- package/dist/renderer-viewport.d.ts +45 -0
- package/dist/renderer-viewport.d.ts.map +1 -1
- package/dist/renderer-viewport.js +44 -0
- package/dist/renderer-viewport.js.map +1 -1
- package/dist/rte-clip-space.d.ts +12 -0
- package/dist/rte-clip-space.d.ts.map +1 -0
- package/dist/rte-clip-space.js +22 -0
- package/dist/rte-clip-space.js.map +1 -0
- package/dist/rte-frustum.d.ts +24 -0
- package/dist/rte-frustum.d.ts.map +1 -0
- package/dist/rte-frustum.js +45 -0
- package/dist/rte-frustum.js.map +1 -0
- package/dist/scene-batch-upload.d.ts.map +1 -1
- package/dist/scene-batch-upload.js +14 -38
- package/dist/scene-batch-upload.js.map +1 -1
- package/dist/scene-device-recovery.d.ts.map +1 -1
- package/dist/scene-device-recovery.js +5 -9
- package/dist/scene-device-recovery.js.map +1 -1
- package/dist/scene-finalize-regroup.d.ts +54 -0
- package/dist/scene-finalize-regroup.d.ts.map +1 -0
- package/dist/scene-finalize-regroup.js +136 -0
- package/dist/scene-finalize-regroup.js.map +1 -0
- package/dist/scene-instance-bounds.d.ts +12 -0
- package/dist/scene-instance-bounds.d.ts.map +1 -0
- package/dist/scene-instance-bounds.js +48 -0
- package/dist/scene-instance-bounds.js.map +1 -0
- package/dist/scene-instance-materialization.d.ts +4 -1
- package/dist/scene-instance-materialization.d.ts.map +1 -1
- package/dist/scene-instance-materialization.js +21 -6
- package/dist/scene-instance-materialization.js.map +1 -1
- package/dist/scene-instance-translation.d.ts +17 -0
- package/dist/scene-instance-translation.d.ts.map +1 -0
- package/dist/scene-instance-translation.js +50 -0
- package/dist/scene-instance-translation.js.map +1 -0
- package/dist/scene-instance-types.d.ts +6 -0
- package/dist/scene-instance-types.d.ts.map +1 -1
- package/dist/scene-model-translation.d.ts +2 -0
- package/dist/scene-model-translation.d.ts.map +1 -1
- package/dist/scene-model-translation.js +1 -1
- package/dist/scene-model-translation.js.map +1 -1
- package/dist/scene-rect-select.d.ts +11 -1
- package/dist/scene-rect-select.d.ts.map +1 -1
- package/dist/scene-rect-select.js +7 -4
- package/dist/scene-rect-select.js.map +1 -1
- package/dist/scene.d.ts +27 -20
- package/dist/scene.d.ts.map +1 -1
- package/dist/scene.js +109 -238
- package/dist/scene.js.map +1 -1
- package/dist/section-2d-lift.d.ts +10 -2
- package/dist/section-2d-lift.d.ts.map +1 -1
- package/dist/section-2d-lift.js +14 -6
- package/dist/section-2d-lift.js.map +1 -1
- package/dist/section-2d-line-buffer.d.ts +12 -4
- package/dist/section-2d-line-buffer.d.ts.map +1 -1
- package/dist/section-2d-line-buffer.js +59 -33
- package/dist/section-2d-line-buffer.js.map +1 -1
- package/dist/section-2d-overlay.d.ts +18 -72
- package/dist/section-2d-overlay.d.ts.map +1 -1
- package/dist/section-2d-overlay.js +72 -103
- package/dist/section-2d-overlay.js.map +1 -1
- package/dist/section-2d-types.d.ts +38 -0
- package/dist/section-2d-types.d.ts.map +1 -0
- package/dist/section-2d-types.js +6 -0
- package/dist/section-2d-types.js.map +1 -0
- package/dist/section-cap-pipelines.d.ts +6 -0
- package/dist/section-cap-pipelines.d.ts.map +1 -0
- package/dist/section-cap-pipelines.js +87 -0
- package/dist/section-cap-pipelines.js.map +1 -0
- package/dist/section-plane-geometry.d.ts +24 -0
- package/dist/section-plane-geometry.d.ts.map +1 -0
- package/dist/section-plane-geometry.js +93 -0
- package/dist/section-plane-geometry.js.map +1 -0
- package/dist/section-plane.d.ts +26 -24
- package/dist/section-plane.d.ts.map +1 -1
- package/dist/section-plane.js +57 -182
- package/dist/section-plane.js.map +1 -1
- package/dist/shaders/ao.wgsl.d.ts +2 -0
- package/dist/shaders/ao.wgsl.d.ts.map +1 -0
- package/dist/shaders/ao.wgsl.js +234 -0
- package/dist/shaders/ao.wgsl.js.map +1 -0
- package/dist/shaders/color-transfer.wgsl.d.ts +28 -0
- package/dist/shaders/color-transfer.wgsl.d.ts.map +1 -0
- package/dist/shaders/color-transfer.wgsl.js +55 -0
- package/dist/shaders/color-transfer.wgsl.js.map +1 -0
- package/dist/shaders/depth-reconstruct.wgsl.d.ts +16 -0
- package/dist/shaders/depth-reconstruct.wgsl.d.ts.map +1 -0
- package/dist/shaders/depth-reconstruct.wgsl.js +69 -0
- package/dist/shaders/depth-reconstruct.wgsl.js.map +1 -0
- package/dist/shaders/edges.wgsl.d.ts +2 -0
- package/dist/shaders/edges.wgsl.d.ts.map +1 -0
- package/dist/shaders/edges.wgsl.js +158 -0
- package/dist/shaders/edges.wgsl.js.map +1 -0
- package/dist/shaders/main-rte-shadow.wgsl.d.ts +4 -0
- package/dist/shaders/main-rte-shadow.wgsl.d.ts.map +1 -0
- package/dist/shaders/main-rte-shadow.wgsl.js +54 -0
- package/dist/shaders/main-rte-shadow.wgsl.js.map +1 -0
- package/dist/shaders/main.wgsl.d.ts +1 -6
- package/dist/shaders/main.wgsl.d.ts.map +1 -1
- package/dist/shaders/main.wgsl.js +183 -228
- package/dist/shaders/main.wgsl.js.map +1 -1
- package/dist/shaders/relative-to-eye.wgsl.d.ts +12 -0
- package/dist/shaders/relative-to-eye.wgsl.d.ts.map +1 -0
- package/dist/shaders/relative-to-eye.wgsl.js +40 -0
- package/dist/shaders/relative-to-eye.wgsl.js.map +1 -0
- package/dist/shaders/section-2d-overlay.wgsl.d.ts +28 -12
- package/dist/shaders/section-2d-overlay.wgsl.d.ts.map +1 -1
- package/dist/shaders/section-2d-overlay.wgsl.js +40 -13
- package/dist/shaders/section-2d-overlay.wgsl.js.map +1 -1
- package/dist/shaders/shadow.wgsl.d.ts +1 -1
- package/dist/shaders/shadow.wgsl.d.ts.map +1 -1
- package/dist/shaders/shadow.wgsl.js +25 -7
- package/dist/shaders/shadow.wgsl.js.map +1 -1
- package/dist/shaders/sky.wgsl.d.ts +1 -11
- package/dist/shaders/sky.wgsl.d.ts.map +1 -1
- package/dist/shaders/sky.wgsl.js +17 -19
- package/dist/shaders/sky.wgsl.js.map +1 -1
- package/dist/shaders/specular.wgsl.d.ts +34 -0
- package/dist/shaders/specular.wgsl.d.ts.map +1 -0
- package/dist/shaders/specular.wgsl.js +134 -0
- package/dist/shaders/specular.wgsl.js.map +1 -0
- package/dist/shaders/symbolic-overlay.wgsl.d.ts +2 -9
- package/dist/shaders/symbolic-overlay.wgsl.d.ts.map +1 -1
- package/dist/shaders/symbolic-overlay.wgsl.js +88 -17
- package/dist/shaders/symbolic-overlay.wgsl.js.map +1 -1
- package/dist/shaders/textured.wgsl.d.ts.map +1 -1
- package/dist/shaders/textured.wgsl.js +11 -9
- package/dist/shaders/textured.wgsl.js.map +1 -1
- package/dist/shadow-light-matrix.d.ts +7 -0
- package/dist/shadow-light-matrix.d.ts.map +1 -1
- package/dist/shadow-light-matrix.js +11 -0
- package/dist/shadow-light-matrix.js.map +1 -1
- package/dist/shadow-occluder-batches.d.ts +10 -0
- package/dist/shadow-occluder-batches.d.ts.map +1 -0
- package/dist/shadow-occluder-batches.js +39 -0
- package/dist/shadow-occluder-batches.js.map +1 -0
- package/dist/shadow-occluders.d.ts.map +1 -1
- package/dist/shadow-occluders.js +4 -0
- package/dist/shadow-occluders.js.map +1 -1
- package/dist/shadow-pass.d.ts +5 -51
- package/dist/shadow-pass.d.ts.map +1 -1
- package/dist/shadow-pass.js +49 -45
- package/dist/shadow-pass.js.map +1 -1
- package/dist/shadow-types.d.ts +39 -0
- package/dist/shadow-types.d.ts.map +1 -0
- package/dist/shadow-types.js +15 -0
- package/dist/shadow-types.js.map +1 -0
- package/dist/symbolic-overlay-geometry.d.ts +7 -0
- package/dist/symbolic-overlay-geometry.d.ts.map +1 -0
- package/dist/symbolic-overlay-geometry.js +43 -0
- package/dist/symbolic-overlay-geometry.js.map +1 -0
- package/dist/symbolic-overlay-pipelines.d.ts +24 -36
- package/dist/symbolic-overlay-pipelines.d.ts.map +1 -1
- package/dist/symbolic-overlay-pipelines.js +175 -182
- package/dist/symbolic-overlay-pipelines.js.map +1 -1
- package/dist/symbolic-text-atlas.d.ts +21 -0
- package/dist/symbolic-text-atlas.d.ts.map +1 -1
- package/dist/symbolic-text-atlas.js +29 -1
- package/dist/symbolic-text-atlas.js.map +1 -1
- package/dist/types.d.ts +20 -26
- package/dist/types.d.ts.map +1 -1
- package/dist/visual-enhancement.d.ts +30 -1
- package/dist/visual-enhancement.d.ts.map +1 -1
- package/dist/visual-enhancement.js +44 -12
- package/dist/visual-enhancement.js.map +1 -1
- package/package.json +3 -3
- package/dist/post-processor.d.ts +0 -60
- package/dist/post-processor.d.ts.map +0 -1
- package/dist/post-processor.js +0 -353
- package/dist/post-processor.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -11,7 +11,7 @@ export { appearanceSourceTriangle, expandAppearanceCorners, equivalentAppearance
|
|
|
11
11
|
export { sameCompanionParts } from './appearance-companions.js';
|
|
12
12
|
export { invertAppearancePartition, validateAppearancePartition } from './appearance-preview.js';
|
|
13
13
|
import { createReferenceImageManager } from './reference-image-host.js';
|
|
14
|
-
import { resizeRendererViewport } from './renderer-viewport.js';
|
|
14
|
+
import { measureDrawingBuffer, resizeRendererViewport } from './renderer-viewport.js';
|
|
15
15
|
export { pickFitPolicy } from './camera-fit-policy.js';
|
|
16
16
|
// `Scene` is NOT exported: an exported class republishes every method `SceneContents` above just froze.
|
|
17
17
|
export { Picker } from './picker.js';
|
|
@@ -43,6 +43,8 @@ export { hostsOtherEntities } from './mesh-entity-hosting.js';
|
|
|
43
43
|
export { FederationRegistry, federationRegistry } from './federation-registry.js';
|
|
44
44
|
export * from './types.js';
|
|
45
45
|
export { resolveEnvironment, deriveSkyGradient, packEnvironmentUniforms, ENVIRONMENT_UNIFORM_SIZE, } from './environment.js';
|
|
46
|
+
export { DEFAULT_OVERLAY_THEME } from './overlay-theme.js';
|
|
47
|
+
import { DEFAULT_OVERLAY_THEME } from './overlay-theme.js';
|
|
46
48
|
export { resolveContributionThresholdPx, projectedAabbRadiusPx } from './contribution-cull.js';
|
|
47
49
|
export { chunkCellKey, bucketBaseKeyFor, DEFAULT_CHUNK_CELL_SIZE } from './chunk-grid.js';
|
|
48
50
|
export { selectEvictions, MIN_EVICTION_AGE_FRAMES } from './residency.js';
|
|
@@ -79,32 +81,38 @@ import { Scene } from './scene.js';
|
|
|
79
81
|
import { Picker } from './picker.js';
|
|
80
82
|
import { reportableItemId } from './pick-resolve.js';
|
|
81
83
|
import { MathUtils, viewBasis } from './math.js';
|
|
82
|
-
import {
|
|
83
|
-
import { VisualEnhancementResolver } from './visual-enhancement.js';
|
|
84
|
+
import { isRteAabbVisible, rteFrustum, sourceFrustumFromRte } from './rte-frustum.js';
|
|
85
|
+
import { VisualEnhancementResolver, livePostEffects } from './visual-enhancement.js';
|
|
84
86
|
import { packClipBox } from './clip-box.js';
|
|
87
|
+
import { MESH_FLAGS_BYTE_OFFSET, MESH_FLAG_RTE_DRAWABLE, MESH_UNIFORM_FLOATS, MESH_UNIFORM_OFFSET, packRteCameraOrigin, packRteFragmentSpace, } from './mesh-rte-uniforms.js';
|
|
88
|
+
import { packMeshMaterial } from './mesh-material.js';
|
|
85
89
|
import { RendererOverlays } from './renderer-overlays.js';
|
|
86
90
|
import { resolveSectionPlaneFrame } from './render-section-plane.js';
|
|
87
91
|
import { PickingManager } from './picking-manager.js';
|
|
88
92
|
import { RaycastEngine } from './raycast-engine.js';
|
|
89
93
|
import { RenderDegradationMonitor } from './render-degradation.js';
|
|
90
|
-
import {
|
|
94
|
+
import { PostPassChain } from './post-pass-chain.js';
|
|
91
95
|
import { InteractionEffectsGovernor } from './interaction-effects-governor.js';
|
|
92
96
|
import { VisibilityEpochTracker } from './visibility-epoch.js';
|
|
93
97
|
import { isEntityVisible } from './entity-visibility.js';
|
|
94
98
|
import { ModelBoundsTracker } from './model-bounds-tracker.js';
|
|
95
99
|
import { resolveContributionThresholdPx, projectedAabbRadiusPx, projectedInstancedRadiusPx } from './contribution-cull.js';
|
|
96
|
-
import { EdlPass } from './edl-pass.js';
|
|
97
100
|
import { SkyPass } from './sky-pass.js';
|
|
98
101
|
import { skyShaderSource } from './shaders/sky.wgsl.js';
|
|
99
102
|
import { resolveEnvironment } from './environment.js';
|
|
100
103
|
import { ShadowPass, resolveShadowMapResolution } from './shadow-pass.js';
|
|
101
|
-
import { fitSunLightMatrix, cameraFrustumFocusCorners } from './shadow-light-matrix.js';
|
|
102
|
-
import { collectShadowOccluders
|
|
104
|
+
import { fitSunLightMatrix, cameraFrustumFocusCorners, resolveShadowNormalBiasMetres } from './shadow-light-matrix.js';
|
|
105
|
+
import { collectShadowOccluders } from './shadow-occluders.js';
|
|
106
|
+
import { uploadInstancedRteDeltas } from './instanced-rte.js';
|
|
107
|
+
import { shadowOccluderBatches } from './shadow-occluder-batches.js';
|
|
108
|
+
import { captureRendererScreenshot } from './renderer-screenshot.js';
|
|
109
|
+
import { beginRendererColorFrameCapture, cancelRendererColorFrame, discardRendererColorFrameReadback, encodeRendererColorFrameCapture, requestRendererColorFrame, retryRendererColorFrame, settleRendererColorFrameCapture } from './renderer-color-readback.js';
|
|
103
110
|
import { shouldRouteMeshTransparent, shouldRouteBatchTransparent, splitVisibleIdsByPromotion, DEFAULT_GHOST_ALPHA } from './overlay-routing.js';
|
|
104
111
|
import { XRayAlpha, XRayEpochTracker } from './xray-alpha.js';
|
|
105
112
|
import { PartialBatchRequests } from './partial-batch-requests.js';
|
|
106
113
|
import { colorSaltByte, packEntityLane } from './scene-geometry.js';
|
|
107
114
|
import { PointCloudRenderer } from './pointcloud/point-cloud-renderer.js';
|
|
115
|
+
import { appendPointCloudChunk as appendPointCloudChunkImpl, beginPointCloudStream as beginPointCloudStreamImpl, endPointCloudStream as endPointCloudStreamImpl, removePointCloudAsset as removePointCloudAssetImpl, } from './pointcloud/point-cloud-stream-lifecycle.js';
|
|
108
116
|
import { DeviationComputer } from './deviation/deviation-computer.js';
|
|
109
117
|
import { runGuardedGpuUpload, isDeviceLossThrow } from './gpu-upload-guard.js';
|
|
110
118
|
import { recoverRendererDevice, rendererDeviceLostError } from './device-recovery.js';
|
|
@@ -133,15 +141,19 @@ export class Renderer {
|
|
|
133
141
|
scene;
|
|
134
142
|
picker = null;
|
|
135
143
|
canvas;
|
|
144
|
+
// The overlay theme (#5484), kept so a pre-init `setOverlayTheme` call
|
|
145
|
+
// (and a later re-init) still lands — `init()` re-applies it to `pipeline`.
|
|
146
|
+
overlayTheme = DEFAULT_OVERLAY_THEME;
|
|
136
147
|
/**
|
|
137
148
|
* Section-plane gizmo, 2D section drawing/cap, and the standalone 3D line
|
|
138
149
|
* + symbolic annotation overlays (issue #2425). Created here rather than in
|
|
139
|
-
* `init()` so a pre-init `
|
|
150
|
+
* `init()` so a pre-init `setOverlayTheme` still lands — the GPU
|
|
140
151
|
* objects inside stay null until `init()` calls `overlays.init()`.
|
|
141
152
|
*/
|
|
142
153
|
overlays = new RendererOverlays({
|
|
143
154
|
getModelBounds: () => this.getModelBounds(),
|
|
144
155
|
expandModelBoundsWithFlatVertices: (positions, stride) => this.modelBoundsTracker.expandWithFlatVertices(positions, stride),
|
|
156
|
+
expandModelBoundsWithAnchoredLineVertices: (positions, origin, stride) => this.modelBoundsTracker.expandWithAnchoredVertices(positions, origin, stride),
|
|
145
157
|
syncCameraSceneBounds: () => {
|
|
146
158
|
if (this.modelBounds)
|
|
147
159
|
this.camera.setSceneBounds(this.modelBounds);
|
|
@@ -150,9 +162,12 @@ export class Renderer {
|
|
|
150
162
|
});
|
|
151
163
|
referenceImages = createReferenceImageManager(this);
|
|
152
164
|
getReferenceImages() { return this.referenceImages; }
|
|
153
|
-
|
|
165
|
+
// Ambient occlusion, separation lines and eye-dome lighting (post-pass-chain.ts),
|
|
166
|
+
// created on the first rendered frame.
|
|
167
|
+
postPasses = null;
|
|
168
|
+
/** Device px per CSS px in the drawing buffer; CSS-px sizes scale by it (#5383). */
|
|
169
|
+
pixelRatio = 1;
|
|
154
170
|
interactionEffects = new InteractionEffectsGovernor();
|
|
155
|
-
edlPass = null;
|
|
156
171
|
// Procedural sky background — created lazily on the first frame that
|
|
157
172
|
// enables it (most sessions never do).
|
|
158
173
|
skyPass = null;
|
|
@@ -173,6 +188,7 @@ export class Renderer {
|
|
|
173
188
|
pointCloudRenderer = null;
|
|
174
189
|
pointCloudStreamEpoch = 0;
|
|
175
190
|
pointCloudStreamEpochs = new Map(); // handle id → epoch; cleanup rebuilds `{ id }`, cleared per epoch
|
|
191
|
+
pointCloudNextHandleId = 1; // watermark carried across teardown() so a replacement never reissues an id
|
|
176
192
|
/** Set true at the end of the LATEST `init()`; gates `whenReady()` and
|
|
177
193
|
* `isReady()`. Revoked synchronously by `init()` and by `destroy()`, and
|
|
178
194
|
* overridden (not cleared) by a device loss — see `deviceLost`, which the
|
|
@@ -381,13 +397,9 @@ export class Renderer {
|
|
|
381
397
|
// One-shot log guard — prints Y-up clip bounds on first section-enable so
|
|
382
398
|
// users can confirm the slider is operating on the intended range.
|
|
383
399
|
_loggedSectionBounds = false;
|
|
384
|
-
//
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
// 60 floats = the WGSL Uniforms struct incl. quantParams (see
|
|
388
|
-
// pipeline.getUniformBufferSize).
|
|
389
|
-
uniformScratch = new Float32Array(60);
|
|
390
|
-
uniformScratchU32 = new Uint32Array(this.uniformScratch.buffer, 176, 4);
|
|
400
|
+
// One 336-byte buffer serves all frame batches/meshes (84 floats including RTE lanes).
|
|
401
|
+
uniformScratch = new Float32Array(MESH_UNIFORM_FLOATS);
|
|
402
|
+
uniformScratchU32 = new Uint32Array(this.uniformScratch.buffer, MESH_FLAGS_BYTE_OFFSET, 4);
|
|
391
403
|
// What the last render() actually clipped, so the GPU picker can mirror it and
|
|
392
404
|
// section/crop-clipped geometry stays unpickable, not just invisible. Updated
|
|
393
405
|
// every render; read by pick()/pickRect(). null = nothing clipped that frame.
|
|
@@ -409,8 +421,8 @@ export class Renderer {
|
|
|
409
421
|
* are released first. The comment below advertises a `destroy()` + `init()`
|
|
410
422
|
* re-init flow, and the obvious device-loss auto-recovery is to call
|
|
411
423
|
* `init()` on the live instance — which, without this, silently orphaned
|
|
412
|
-
* two render pipelines, the picker, the post
|
|
413
|
-
* deviation pipelines
|
|
424
|
+
* two render pipelines, the picker, the post passes, the point-cloud and
|
|
425
|
+
* deviation pipelines and the overlay layer's glyph atlas, per
|
|
414
426
|
* recovery (#2448). Making the method self-safe is cheaper than trusting
|
|
415
427
|
* every future caller to remember.
|
|
416
428
|
*
|
|
@@ -473,8 +485,8 @@ export class Renderer {
|
|
|
473
485
|
await initializingDevice.init(this.canvas);
|
|
474
486
|
// A `destroy()` (or a newer `init()`) landed while we were parked on the
|
|
475
487
|
// device. Everything below allocates a full GPU stack — two pipelines,
|
|
476
|
-
// the picker, the
|
|
477
|
-
//
|
|
488
|
+
// the picker, the point-cloud and deviation pipelines, the overlay
|
|
489
|
+
// glyph atlas — and this aborted
|
|
478
490
|
// path runs no second teardown, so all of it would be orphaned outright
|
|
479
491
|
// (#2465). `markReady()`'s generation check is not enough on its own: it
|
|
480
492
|
// withholds the readiness PUBLICATION, not the allocation. Release the
|
|
@@ -484,43 +496,32 @@ export class Renderer {
|
|
|
484
496
|
this.device.destroy();
|
|
485
497
|
return;
|
|
486
498
|
}
|
|
487
|
-
//
|
|
488
|
-
//
|
|
489
|
-
//
|
|
490
|
-
|
|
499
|
+
// Size the buffer to the element's device pixels, the same sizing every
|
|
500
|
+
// frame applies (#5383); an unlaid-out canvas keeps its attributes. Both
|
|
501
|
+
// are clamped to the GPU's max 2D texture dimension so the initial
|
|
502
|
+
// pipeline allocations can't overflow on tall/wide layouts.
|
|
491
503
|
const maxDim = this.device.getMaxTextureDimension();
|
|
492
|
-
const
|
|
493
|
-
|
|
494
|
-
const width = Math.min(
|
|
495
|
-
const height = Math.min(
|
|
504
|
+
const measured = measureDrawingBuffer(this.canvas, maxDim);
|
|
505
|
+
this.pixelRatio = measured?.pixelRatio ?? 1;
|
|
506
|
+
const width = Math.max(1, Math.min(measured?.width ?? this.canvas.width, maxDim));
|
|
507
|
+
const height = Math.max(1, Math.min(measured?.height ?? this.canvas.height, maxDim));
|
|
496
508
|
// Set pixel dimensions if not already set, or if we clamped them down
|
|
497
509
|
if (!this.canvas.width || !this.canvas.height || this.canvas.width !== width || this.canvas.height !== height) {
|
|
498
510
|
this.canvas.width = width;
|
|
499
511
|
this.canvas.height = height;
|
|
500
512
|
}
|
|
501
513
|
this.pipeline = new RenderPipeline(this.device, width, height);
|
|
514
|
+
// Re-apply any theme set before this (re)creation, or set by a prior
|
|
515
|
+
// init() before a device-loss re-init, so it isn't lost (#5484).
|
|
516
|
+
this.pipeline.selectionColorUniform.update(this.overlayTheme.selection);
|
|
502
517
|
this.picker = new Picker(this.device, width, height);
|
|
503
518
|
this.overlays.init(this.device.getDevice(), this.device.getFormat(), this.pipeline.getSampleCount());
|
|
504
519
|
this.referenceImages.init(this.device.getDevice(), this.device.getFormat(), this.pipeline.getSampleCount());
|
|
505
|
-
|
|
506
|
-
// depth TEXTURE_BINDING), rendering still works without post-processing.
|
|
507
|
-
try {
|
|
508
|
-
this.postProcessor = new PostProcessor(this.device, {
|
|
509
|
-
enableContactShading: true,
|
|
510
|
-
contactRadius: 1.0,
|
|
511
|
-
contactIntensity: 0.3,
|
|
512
|
-
}, this.pipeline.getSampleCount());
|
|
513
|
-
}
|
|
514
|
-
catch (e) {
|
|
515
|
-
console.warn('[Renderer] PostProcessor init failed (post-processing disabled):', e);
|
|
516
|
-
this.postProcessor = null;
|
|
517
|
-
}
|
|
518
|
-
this.pointCloudRenderer = new PointCloudRenderer(this.device.getDevice(), this.device.getFormat(), 'depth24plus-stencil8', this.pipeline.getSampleCount());
|
|
520
|
+
this.pointCloudRenderer = new PointCloudRenderer(this.device.getDevice(), this.device.getFormat(), 'depth24plus-stencil8', this.pipeline.getSampleCount(), this.pointCloudNextHandleId);
|
|
519
521
|
// Compute pipeline for the BIM↔scan deviation heatmap. Lazily
|
|
520
522
|
// owns the per-triangle BVH GPU buffers; idle until the first
|
|
521
523
|
// `computeDeviations` call.
|
|
522
524
|
this.deviationComputer.init(this.device.getDevice());
|
|
523
|
-
this.edlPass = new EdlPass(this.device, this.pipeline.getSampleCount());
|
|
524
525
|
this.camera.setAspect(width / height);
|
|
525
526
|
// Update picking manager with initialized picker
|
|
526
527
|
this.pickingManager.setPicker(this.picker);
|
|
@@ -707,6 +708,7 @@ export class Renderer {
|
|
|
707
708
|
}
|
|
708
709
|
this.deviceLossSequence++;
|
|
709
710
|
this.deviceLost = true;
|
|
711
|
+
cancelRendererColorFrame(this);
|
|
710
712
|
this.recovery.lostReferenceImages = this.referenceImages.hasImages();
|
|
711
713
|
this.referenceImages.destroy();
|
|
712
714
|
this.deviceLostGeneration = this.initGeneration;
|
|
@@ -766,11 +768,13 @@ export class Renderer {
|
|
|
766
768
|
// regions really do have such a source on a HEALTHY device: the outer
|
|
767
769
|
// one runs `scene.restoreAllEvicted()` for capture frames, the encode
|
|
768
770
|
// one builds visibility sub-batches through
|
|
769
|
-
// `scene.getOrCreatePartialBatch()`, and both
|
|
770
|
-
//
|
|
771
|
-
//
|
|
772
|
-
//
|
|
773
|
-
//
|
|
771
|
+
// `scene.getOrCreatePartialBatch()`, and both merge geometry into
|
|
772
|
+
// fresh typed arrays, whose allocation throws a plain `RangeError`
|
|
773
|
+
// ("Array buffer allocation failed") under host memory pressure.
|
|
774
|
+
// (Before #5429 the GPU upload itself added a second source, the
|
|
775
|
+
// mapped-at-creation `createBuffer` RangeError; uploads now go through
|
|
776
|
+
// `createStaticGpuBuffer`, which cannot raise it.) Latching there
|
|
777
|
+
// would kill the viewport for a failure whose blast radius should be one frame, and
|
|
774
778
|
// would raise a false "graphics device was lost" toast plus false
|
|
775
779
|
// `device_lost` telemetry on top.
|
|
776
780
|
//
|
|
@@ -879,38 +883,16 @@ export class Renderer {
|
|
|
879
883
|
* chunks will arrive (currently a no-op but kept for symmetry).
|
|
880
884
|
*/
|
|
881
885
|
beginPointCloudStream(meta) {
|
|
882
|
-
|
|
883
|
-
throw rendererDeviceLostError();
|
|
884
|
-
if (!this.pointCloudRenderer) {
|
|
885
|
-
throw new Error('Renderer not initialized. Call init() first.');
|
|
886
|
-
}
|
|
887
|
-
const handle = this.pointCloudRenderer.beginAsset(meta);
|
|
888
|
-
this.pointCloudStreamEpochs.set(handle.id, this.pointCloudStreamEpoch);
|
|
889
|
-
return handle;
|
|
890
|
-
}
|
|
891
|
-
currentPointCloudStreamRenderer(handle) {
|
|
892
|
-
if (this.deviceLost || !this.pointCloudRenderer
|
|
893
|
-
|| this.pointCloudStreamEpochs.get(handle.id) !== this.pointCloudStreamEpoch)
|
|
894
|
-
throw rendererDeviceLostError();
|
|
895
|
-
return this.pointCloudRenderer;
|
|
886
|
+
return beginPointCloudStreamImpl(this, meta);
|
|
896
887
|
}
|
|
897
888
|
appendPointCloudChunk(handle, chunk) {
|
|
898
|
-
this
|
|
899
|
-
this.modelBoundsTracker.expandForPointClouds();
|
|
900
|
-
this.camera.setSceneBounds(this.modelBounds);
|
|
901
|
-
this.requestRender();
|
|
889
|
+
appendPointCloudChunkImpl(this, handle, chunk);
|
|
902
890
|
}
|
|
903
891
|
endPointCloudStream(handle) {
|
|
904
|
-
this
|
|
905
|
-
this.requestRender();
|
|
892
|
+
endPointCloudStreamImpl(this, handle);
|
|
906
893
|
}
|
|
907
894
|
removePointCloudAsset(handle) {
|
|
908
|
-
|
|
909
|
-
return;
|
|
910
|
-
this.pointCloudRenderer?.removeAsset(handle);
|
|
911
|
-
// Bounds may have shrunk — recompute from scratch so fit-to-view
|
|
912
|
-
// and section-plane sliders see fresh extents.
|
|
913
|
-
this.refreshPlacementBounds();
|
|
895
|
+
removePointCloudAssetImpl(this, handle);
|
|
914
896
|
}
|
|
915
897
|
/**
|
|
916
898
|
* Reassign a streamed point-cloud's expressId after upload. Use
|
|
@@ -958,6 +940,13 @@ export class Renderer {
|
|
|
958
940
|
this.clearCaches();
|
|
959
941
|
this.refreshPlacementBounds();
|
|
960
942
|
}
|
|
943
|
+
/** Toggle one resident streamed scan without releasing its GPU buffers. */
|
|
944
|
+
setPointCloudVisibility(handle, visible) {
|
|
945
|
+
if (!this.pointCloudRenderer?.setAssetVisible(handle, visible))
|
|
946
|
+
return;
|
|
947
|
+
this.clearCaches();
|
|
948
|
+
this.refreshPlacementBounds();
|
|
949
|
+
}
|
|
961
950
|
refreshPlacementBounds() {
|
|
962
951
|
this.modelBoundsTracker.recompute();
|
|
963
952
|
this.camera.setSceneBounds(this.modelBounds);
|
|
@@ -1157,65 +1146,7 @@ export class Renderer {
|
|
|
1157
1146
|
* collector drops them.
|
|
1158
1147
|
*/
|
|
1159
1148
|
shadowOccluderBatches(options, device, hasVisibilityFiltering) {
|
|
1160
|
-
|
|
1161
|
-
if (!hasVisibilityFiltering) {
|
|
1162
|
-
for (const batch of all)
|
|
1163
|
-
this.noteShadowOccluderResidency(batch);
|
|
1164
|
-
return all;
|
|
1165
|
-
}
|
|
1166
|
-
const pipeline = this.pipeline;
|
|
1167
|
-
const out = [];
|
|
1168
|
-
for (const batch of all) {
|
|
1169
|
-
const vis = classifyBatchVisibility(batch.expressIds, options.hiddenIds, options.isolatedIds);
|
|
1170
|
-
if (vis.kind === 'none')
|
|
1171
|
-
continue; // fully hidden → does not cast
|
|
1172
|
-
if (vis.kind === 'all') {
|
|
1173
|
-
this.noteShadowOccluderResidency(batch);
|
|
1174
|
-
out.push(batch); // fully visible → its own buffers
|
|
1175
|
-
continue;
|
|
1176
|
-
}
|
|
1177
|
-
// Partially hidden. Transparent parents don't cast (collector's alpha
|
|
1178
|
-
// filter) — skip rather than build a wasted, divergent-key clone.
|
|
1179
|
-
if (batch.color[3] < DEFAULT_MIN_CAST_ALPHA)
|
|
1180
|
-
continue;
|
|
1181
|
-
// The partial sub-batch is built from the PARENT's CPU meshData, so a
|
|
1182
|
-
// cold parent yields nothing until it is restored. Queue that restore
|
|
1183
|
-
// here too: the colour pass only queues it for parents inside its own
|
|
1184
|
-
// frustum, but an up-sun occluder behind the camera still has to cast.
|
|
1185
|
-
this.noteShadowOccluderResidency(batch);
|
|
1186
|
-
// Opaque partial: reuse the colour pass's cached sub-batch. The key is
|
|
1187
|
-
// visibility-content-independent; `_partialBatchEpoch` invalidates it on
|
|
1188
|
-
// any hide/isolate or override change, so the clone is always current.
|
|
1189
|
-
// Without a pipeline the renderer isn't drawing, so skip (the shadow
|
|
1190
|
-
// pass won't run either); casting the whole parent would be wrong.
|
|
1191
|
-
if (!pipeline)
|
|
1192
|
-
continue;
|
|
1193
|
-
const sub = this.scene.getOrCreatePartialBatch(`${batch.colorKey}:${batch.id}`, batch.colorKey, vis.visibleIds, device, pipeline, this._partialBatchEpoch);
|
|
1194
|
-
// A cold parent yields an empty partial (its residency restore is queued
|
|
1195
|
-
// above); skip this frame — the collector drops zero-index draws anyway,
|
|
1196
|
-
// and the subset casts once resident.
|
|
1197
|
-
if (sub && sub.indexCount > 0)
|
|
1198
|
-
out.push(sub);
|
|
1199
|
-
}
|
|
1200
|
-
return out;
|
|
1201
|
-
}
|
|
1202
|
-
/**
|
|
1203
|
-
* Keep a shadow occluder batch resident so it does not thin out silently on
|
|
1204
|
-
* large models under the GPU residency budget (#2670 review). The depth pass
|
|
1205
|
-
* reads these batches' buffers, but that read did not count as usage, so an
|
|
1206
|
-
* up-sun occluder outside the colour frustum aged into an eviction candidate.
|
|
1207
|
-
* Transparent batches never cast (the collector's alpha filter), so their
|
|
1208
|
-
* residency is irrelevant here.
|
|
1209
|
-
*/
|
|
1210
|
-
noteShadowOccluderResidency(batch) {
|
|
1211
|
-
if (batch.color[3] < DEFAULT_MIN_CAST_ALPHA)
|
|
1212
|
-
return;
|
|
1213
|
-
if (batch.gpuResident === false) {
|
|
1214
|
-
this.scene.requestBatchResidency(batch);
|
|
1215
|
-
}
|
|
1216
|
-
else {
|
|
1217
|
-
this.scene.recordBatchDrawn(batch);
|
|
1218
|
-
}
|
|
1149
|
+
return shadowOccluderBatches(this.scene, options, device, hasVisibilityFiltering, this.pipeline, this._partialBatchEpoch);
|
|
1219
1150
|
}
|
|
1220
1151
|
/** Guarded entry point (#4885) for the unguarded body below. */
|
|
1221
1152
|
createMeshFromData(meshData) {
|
|
@@ -1234,18 +1165,17 @@ export class Renderer {
|
|
|
1234
1165
|
const interleaved = new Float32Array(interleavedRaw);
|
|
1235
1166
|
const interleavedU32 = new Uint32Array(interleavedRaw);
|
|
1236
1167
|
// Build this individual mesh (selection highlight + GPU object-id picker)
|
|
1237
|
-
// in
|
|
1168
|
+
// in the same small local frame as its source batch.
|
|
1238
1169
|
// CRITICAL: replicate the BATCH's exact two-step f32 path so the highlight
|
|
1239
1170
|
// is bit-coincident with its source surface (no z-fight, no depth bias):
|
|
1240
1171
|
// batch stores s = f32(local + (origin - sharedOrigin)) [merge]
|
|
1241
|
-
// batch shader
|
|
1242
|
-
// We
|
|
1243
|
-
//
|
|
1172
|
+
// batch shader RTE = sharedOrigin - eye + s [draw]
|
|
1173
|
+
// We retain `s` and the canonical origin separately. When there is no
|
|
1174
|
+
// shared origin, retain the piece origin instead of folding it in.
|
|
1244
1175
|
const o = meshData.origin;
|
|
1245
1176
|
const so = this.scene.getSharedFrameOrigin(meshData.modelIndex, meshData);
|
|
1246
1177
|
const ox = o ? o[0] : 0, oy = o ? o[1] : 0, oz = o ? o[2] : 0;
|
|
1247
1178
|
const fr = Math.fround;
|
|
1248
|
-
const sox = so ? fr(so[0]) : null, soy = so ? fr(so[1]) : 0, soz = so ? fr(so[2]) : 0;
|
|
1249
1179
|
const dx = so ? (ox - so[0]) : ox, dy = so ? (oy - so[1]) : oy, dz = so ? (oz - so[2]) : oz;
|
|
1250
1180
|
// Quantized batches (issue #1682 phase 6) render lattice-snapped
|
|
1251
1181
|
// positions: the shader's quantMin + q*step is exactly the lattice
|
|
@@ -1262,9 +1192,9 @@ export class Renderer {
|
|
|
1262
1192
|
for (let i = 0; i < vertexCount; i++) {
|
|
1263
1193
|
const base = i * 7;
|
|
1264
1194
|
const posBase = i * 3;
|
|
1265
|
-
interleaved[base] = so ?
|
|
1266
|
-
interleaved[base + 1] = so ?
|
|
1267
|
-
interleaved[base + 2] = so ?
|
|
1195
|
+
interleaved[base] = so ? snap(fr(p[posBase] + dx)) : snap(p[posBase]);
|
|
1196
|
+
interleaved[base + 1] = so ? snap(fr(p[posBase + 1] + dy)) : snap(p[posBase + 1]);
|
|
1197
|
+
interleaved[base + 2] = so ? snap(fr(p[posBase + 2] + dz)) : snap(p[posBase + 2]);
|
|
1268
1198
|
const hasNormals = meshData.normals.length > 0;
|
|
1269
1199
|
interleaved[base + 3] = hasNormals ? meshData.normals[posBase] : 0;
|
|
1270
1200
|
interleaved[base + 4] = hasNormals ? meshData.normals[posBase + 1] : 0;
|
|
@@ -1294,7 +1224,9 @@ export class Renderer {
|
|
|
1294
1224
|
usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
|
|
1295
1225
|
});
|
|
1296
1226
|
device.queue.writeBuffer(indexBuffer, 0, meshData.indices);
|
|
1297
|
-
//
|
|
1227
|
+
// Keep the hydrated mesh in its decoded local frame. Folding this
|
|
1228
|
+
// origin back into f32 vertices used to make selection/highlight the
|
|
1229
|
+
// only mesh path that lost centimetres at national-grid coordinates.
|
|
1298
1230
|
// Flagged `hydrated` so it can be freed when its entity leaves the
|
|
1299
1231
|
// selection — these duplicate geometry already drawn by a batch and would
|
|
1300
1232
|
// otherwise accumulate + double-draw (see Scene.disposeHydratedMeshesExcept).
|
|
@@ -1309,7 +1241,14 @@ export class Renderer {
|
|
|
1309
1241
|
vertexBuffer,
|
|
1310
1242
|
indexBuffer,
|
|
1311
1243
|
indexCount: meshData.indices.length,
|
|
1312
|
-
transform:
|
|
1244
|
+
transform: (() => {
|
|
1245
|
+
const transform = MathUtils.identity();
|
|
1246
|
+
transform.m[12] = so ? so[0] : ox;
|
|
1247
|
+
transform.m[13] = so ? so[1] : oy;
|
|
1248
|
+
transform.m[14] = so ? so[2] : oz;
|
|
1249
|
+
return transform;
|
|
1250
|
+
})(),
|
|
1251
|
+
rteOrigin: so ? [so[0], so[1], so[2]] : [ox, oy, oz],
|
|
1313
1252
|
color: meshData.color,
|
|
1314
1253
|
hydrated: true,
|
|
1315
1254
|
});
|
|
@@ -1469,6 +1408,7 @@ export class Renderer {
|
|
|
1469
1408
|
// emit a stream of validation errors. Stay quiet until re-init.
|
|
1470
1409
|
if (this.deviceLost) {
|
|
1471
1410
|
this._renderSkipCount++;
|
|
1411
|
+
cancelRendererColorFrame(this);
|
|
1472
1412
|
return;
|
|
1473
1413
|
}
|
|
1474
1414
|
try {
|
|
@@ -1480,6 +1420,7 @@ export class Renderer {
|
|
|
1480
1420
|
// as "did not fail" — see `frameContainedThrow`.
|
|
1481
1421
|
if (!this.frameContainedThrow)
|
|
1482
1422
|
this.consecutiveDegradedFrames = 0;
|
|
1423
|
+
retryRendererColorFrame(this, () => this.requestRender());
|
|
1483
1424
|
}
|
|
1484
1425
|
catch (error) {
|
|
1485
1426
|
// Safari (26.5) reports device loss SYNCHRONOUSLY: a call against a
|
|
@@ -1495,6 +1436,7 @@ export class Renderer {
|
|
|
1495
1436
|
// host down with us".
|
|
1496
1437
|
this._renderSkipCount++;
|
|
1497
1438
|
this.containFrameThrow(error, 'frame');
|
|
1439
|
+
cancelRendererColorFrame(this);
|
|
1498
1440
|
}
|
|
1499
1441
|
}
|
|
1500
1442
|
/**
|
|
@@ -1506,23 +1448,16 @@ export class Renderer {
|
|
|
1506
1448
|
this._renderSkipCount++;
|
|
1507
1449
|
return;
|
|
1508
1450
|
}
|
|
1509
|
-
//
|
|
1510
|
-
//
|
|
1511
|
-
|
|
1512
|
-
//
|
|
1513
|
-
|
|
1514
|
-
const rect = this.canvas.getBoundingClientRect();
|
|
1515
|
-
const maxDim = this.device.getMaxTextureDimension();
|
|
1516
|
-
const rawWidth = Math.max(1, Math.floor(rect.width));
|
|
1517
|
-
const widthAligned = Math.max(64, Math.floor(rawWidth / 64) * 64);
|
|
1518
|
-
const width = Math.min(widthAligned, Math.floor(maxDim / 64) * 64);
|
|
1519
|
-
const rawHeight = Math.max(1, Math.floor(rect.height));
|
|
1520
|
-
const height = Math.min(rawHeight, maxDim);
|
|
1521
|
-
// Skip rendering if canvas is too small
|
|
1522
|
-
if (width < 64 || height < 10) {
|
|
1451
|
+
// Drawing buffer = the element's device-pixel size (capped ratio, clamped
|
|
1452
|
+
// to the GPU's max texture dimension); see computeDrawingBufferSize (#5383).
|
|
1453
|
+
const measured = measureDrawingBuffer(this.canvas, this.device.getMaxTextureDimension());
|
|
1454
|
+
// Skip rendering while the canvas is collapsed or too small.
|
|
1455
|
+
if (!measured || measured.height < 10) {
|
|
1523
1456
|
this._renderSkipCount++;
|
|
1524
1457
|
return;
|
|
1525
1458
|
}
|
|
1459
|
+
const { width, height } = measured;
|
|
1460
|
+
this.pixelRatio = measured.pixelRatio;
|
|
1526
1461
|
// Update canvas pixel dimensions if needed
|
|
1527
1462
|
const dimensionsChanged = this.canvas.width !== width || this.canvas.height !== height;
|
|
1528
1463
|
if (dimensionsChanged) {
|
|
@@ -1546,6 +1481,13 @@ export class Renderer {
|
|
|
1546
1481
|
}
|
|
1547
1482
|
const device = this.device.getDevice();
|
|
1548
1483
|
const viewProj = this.camera.getViewProjMatrix().m;
|
|
1484
|
+
const relativeToEyeFrame = this.camera.getRelativeToEyeFrame();
|
|
1485
|
+
// Culling must share the translation-free RTE frame used by every GPU
|
|
1486
|
+
// pass. `sourceFrustum` is the f64 plane equivalent for the immutable
|
|
1487
|
+
// source-space BVH; render-time boxes stay eye-relative below.
|
|
1488
|
+
const rteCullFrustum = rteFrustum(relativeToEyeFrame);
|
|
1489
|
+
const rteCullCamera = relativeToEyeFrame.getCameraWorld();
|
|
1490
|
+
const sourceCullFrustum = sourceFrustumFromRte(rteCullFrustum, rteCullCamera);
|
|
1549
1491
|
// Frame stats (issue #1682): geometry draw calls + per-frame cull
|
|
1550
1492
|
// outcomes, snapshotted into _lastFrameStats before queue.submit.
|
|
1551
1493
|
let frameDrawCalls = 0;
|
|
@@ -1585,11 +1527,8 @@ export class Renderer {
|
|
|
1585
1527
|
const edgeIntensity = Math.min(3.0, Math.max(0.0, visualEnhancement.edgeContrast.intensity));
|
|
1586
1528
|
const edgeEnabledU32 = edgeEnabled ? 1 : 0;
|
|
1587
1529
|
const edgeIntensityMilliU32 = Math.round(edgeIntensity * 1000);
|
|
1588
|
-
|
|
1589
|
-
const
|
|
1590
|
-
&& visualEnhancement.separationLines.enabled
|
|
1591
|
-
&& visualEnhancement.separationLines.quality !== 'off';
|
|
1592
|
-
const needsObjectIdPass = contactEnabled || separationEnabled;
|
|
1530
|
+
// Only the edge pass reads the object-id attachment after the pass.
|
|
1531
|
+
const needsObjectIdPass = livePostEffects(visualEnhancement, effectsLive).edges;
|
|
1593
1532
|
// Check if visibility filtering is active
|
|
1594
1533
|
const hasHiddenFilter = options.hiddenIds && options.hiddenIds.size > 0;
|
|
1595
1534
|
const hasIsolatedFilter = options.isolatedIds !== null && options.isolatedIds !== undefined;
|
|
@@ -1697,8 +1636,7 @@ export class Renderer {
|
|
|
1697
1636
|
// Frustum culling (if enabled and spatial index available)
|
|
1698
1637
|
if (options.enableFrustumCulling && options.spatialIndex) {
|
|
1699
1638
|
try {
|
|
1700
|
-
const
|
|
1701
|
-
const visibleIds = new Set(options.spatialIndex.queryFrustum(frustum));
|
|
1639
|
+
const visibleIds = new Set(options.spatialIndex.queryFrustum(sourceCullFrustum));
|
|
1702
1640
|
meshes = meshes.filter(mesh => visibleIds.has(mesh.expressId));
|
|
1703
1641
|
}
|
|
1704
1642
|
catch (error) {
|
|
@@ -1739,12 +1677,15 @@ export class Renderer {
|
|
|
1739
1677
|
}
|
|
1740
1678
|
return; // Skip this frame, context will be reconfigured next frame
|
|
1741
1679
|
}
|
|
1680
|
+
let colorCapture = null;
|
|
1681
|
+
let colorReadback = null;
|
|
1742
1682
|
try {
|
|
1743
1683
|
const clearColor = options.clearColor
|
|
1744
1684
|
? (Array.isArray(options.clearColor)
|
|
1745
1685
|
? { r: options.clearColor[0], g: options.clearColor[1], b: options.clearColor[2], a: options.clearColor[3] }
|
|
1746
1686
|
: options.clearColor)
|
|
1747
1687
|
: { r: 0.1, g: 0.1, b: 0.1, a: 1 };
|
|
1688
|
+
colorCapture = beginRendererColorFrameCapture(this, currentTexture, this.canvas.width, this.canvas.height, this.device.getFormat());
|
|
1748
1689
|
const textureView = currentTexture.createView();
|
|
1749
1690
|
const objectIdView = this.pipeline.getObjectIdTextureView();
|
|
1750
1691
|
// Separate meshes into opaque and transparent
|
|
@@ -1818,6 +1759,8 @@ export class Renderer {
|
|
|
1818
1759
|
for (const mesh of allMeshes) {
|
|
1819
1760
|
if (mesh.uniformBuffer) {
|
|
1820
1761
|
meshBuf.set(viewProj, 0);
|
|
1762
|
+
relativeToEyeFrame.packUniforms(meshBuf, MESH_UNIFORM_OFFSET.rteViewProj);
|
|
1763
|
+
packRteCameraOrigin(relativeToEyeFrame, meshBuf);
|
|
1821
1764
|
meshBuf.set(mesh.transform.m, 16);
|
|
1822
1765
|
// Check if mesh is selected (single or multi-selection)
|
|
1823
1766
|
// For multi-model support: also check modelIndex if provided
|
|
@@ -1830,10 +1773,7 @@ export class Renderer {
|
|
|
1830
1773
|
meshBuf[34] = mesh.color[2];
|
|
1831
1774
|
// Selected meshes always keep their own alpha so highlights stay opaque
|
|
1832
1775
|
meshBuf[35] = isSelected ? mesh.color[3] : alphaForMesh(mesh.expressId, mesh.color[3]);
|
|
1833
|
-
meshBuf[
|
|
1834
|
-
meshBuf[37] = mesh.material?.roughness ?? 0.6;
|
|
1835
|
-
meshBuf[38] = 0;
|
|
1836
|
-
meshBuf[39] = 0;
|
|
1776
|
+
packMeshMaterial(meshBuf, mesh.color[3], mesh.material);
|
|
1837
1777
|
// Section plane data (offset 40-43)
|
|
1838
1778
|
if (sectionPlaneData) {
|
|
1839
1779
|
meshBuf[40] = sectionPlaneData.normal[0];
|
|
@@ -1858,6 +1798,13 @@ export class Renderer {
|
|
|
1858
1798
|
clipBit;
|
|
1859
1799
|
meshFlags[2] = edgeEnabledU32;
|
|
1860
1800
|
meshFlags[3] = edgeIntensityMilliU32;
|
|
1801
|
+
// Individual meshes retain their origin and share the RTE
|
|
1802
|
+
// fragment contract with colour batches.
|
|
1803
|
+
packRteFragmentSpace(relativeToEyeFrame, sectionPlaneData, options.clipBox, meshBuf);
|
|
1804
|
+
const meshOrigin = mesh.rteOrigin
|
|
1805
|
+
?? [mesh.transform.m[12], mesh.transform.m[13], mesh.transform.m[14]];
|
|
1806
|
+
relativeToEyeFrame.packDrawableOrigin(meshOrigin, meshBuf, MESH_UNIFORM_OFFSET.drawableDelta);
|
|
1807
|
+
meshFlags[0] |= MESH_FLAG_RTE_DRAWABLE;
|
|
1861
1808
|
device.queue.writeBuffer(mesh.uniformBuffer, 0, meshBuf);
|
|
1862
1809
|
}
|
|
1863
1810
|
}
|
|
@@ -1887,8 +1834,16 @@ export class Renderer {
|
|
|
1887
1834
|
else {
|
|
1888
1835
|
this.shadowPass.setResolution(resolution);
|
|
1889
1836
|
}
|
|
1890
|
-
|
|
1891
|
-
const
|
|
1837
|
+
// Fit shadows in the same eye-relative frame as vertices.
|
|
1838
|
+
const shadowEye = relativeToEyeFrame.getCameraWorld();
|
|
1839
|
+
const sourceBoundsMin = [bounds.min.x, bounds.min.y, bounds.min.z];
|
|
1840
|
+
const sourceBoundsMax = [bounds.max.x, bounds.max.y, bounds.max.z];
|
|
1841
|
+
const boundsMin = [
|
|
1842
|
+
bounds.min.x - shadowEye[0], bounds.min.y - shadowEye[1], bounds.min.z - shadowEye[2],
|
|
1843
|
+
];
|
|
1844
|
+
const boundsMax = [
|
|
1845
|
+
bounds.max.x - shadowEye[0], bounds.max.y - shadowEye[1], bounds.max.z - shadowEye[2],
|
|
1846
|
+
];
|
|
1892
1847
|
// Lateral shadow fit. AT REST: fit to the camera frustum
|
|
1893
1848
|
// clipped to the model (maintainer #1) so a small building on
|
|
1894
1849
|
// a large site keeps sharp shadows instead of spending the
|
|
@@ -1911,9 +1866,13 @@ export class Renderer {
|
|
|
1911
1866
|
aspect: this.canvas.height > 0 ? this.canvas.width / this.canvas.height : 1,
|
|
1912
1867
|
ortho: this.camera.getProjectionMode() === 'orthographic',
|
|
1913
1868
|
orthoHalfHeight: this.camera.getOrthoSize(),
|
|
1914
|
-
boundsMin,
|
|
1915
|
-
boundsMax,
|
|
1916
|
-
})
|
|
1869
|
+
boundsMin: sourceBoundsMin,
|
|
1870
|
+
boundsMax: sourceBoundsMax,
|
|
1871
|
+
})?.map((corner) => ({
|
|
1872
|
+
x: corner.x - shadowEye[0],
|
|
1873
|
+
y: corner.y - shadowEye[1],
|
|
1874
|
+
z: corner.z - shadowEye[2],
|
|
1875
|
+
}));
|
|
1917
1876
|
}
|
|
1918
1877
|
const sun = resolveEnvironment(options.environment).sunDirection;
|
|
1919
1878
|
const fit = fitSunLightMatrix({ sunDirection: sun, boundsMin, boundsMax, focusCorners });
|
|
@@ -1948,7 +1907,7 @@ export class Renderer {
|
|
|
1948
1907
|
box: options.clipBox?.enabled
|
|
1949
1908
|
? { min: options.clipBox.min, max: options.clipBox.max }
|
|
1950
1909
|
: null,
|
|
1951
|
-
});
|
|
1910
|
+
}, { cameraWorld: shadowEye });
|
|
1952
1911
|
// Shadow uniform: light matrix + sampling params. The kernel
|
|
1953
1912
|
// width follows the sun's angular size (physical, ~0.53°
|
|
1954
1913
|
// like Blender's Sun lamp). Bias scales with the kernel: a
|
|
@@ -1958,7 +1917,7 @@ export class Renderer {
|
|
|
1958
1917
|
const texelWorld = (2 * fit.orthoHalfWidth) / resolution;
|
|
1959
1918
|
const sunAngleDeg = shadowOpts.sunAngleDeg ?? 0.53;
|
|
1960
1919
|
const pcfRadius = Math.min(Math.max(sunAngleDeg * 3.0, 0.75), 8.0);
|
|
1961
|
-
const normalBias = texelWorld
|
|
1920
|
+
const normalBias = resolveShadowNormalBiasMetres(texelWorld, pcfRadius);
|
|
1962
1921
|
const s = this.shadowScratch;
|
|
1963
1922
|
s.set(fit.lightViewProj.m, 0);
|
|
1964
1923
|
s[16] = 1 / resolution; // texelSize
|
|
@@ -2094,7 +2053,7 @@ export class Renderer {
|
|
|
2094
2053
|
if (allBatchedMeshes.length > 0 || this.scene.getTexturedMeshes().length > 0 || this.scene.getInstancedTemplates().length > 0) {
|
|
2095
2054
|
// Frustum culling for batched meshes - skip entire batches outside the camera view
|
|
2096
2055
|
// This is the primary performance optimization for large models (200K+ meshes)
|
|
2097
|
-
const frustum =
|
|
2056
|
+
const frustum = rteCullFrustum;
|
|
2098
2057
|
// Contribution culling (issue #1682): skip batches whose world
|
|
2099
2058
|
// AABB projects below a pixel threshold. Disabled unless the
|
|
2100
2059
|
// caller opts in via options.contributionCull; the threshold is
|
|
@@ -2121,7 +2080,7 @@ export class Renderer {
|
|
|
2121
2080
|
mode: this.camera.getProjectionMode(),
|
|
2122
2081
|
fovYRadians: this.camera.getFOV(),
|
|
2123
2082
|
orthoHalfHeight: this.camera.getOrthoSize(),
|
|
2124
|
-
viewportHeightPx: this.canvas.height,
|
|
2083
|
+
viewportHeightPx: this.canvas.height / this.pixelRatio, // CSS px, like the thresholds
|
|
2125
2084
|
};
|
|
2126
2085
|
}
|
|
2127
2086
|
// Per-batch visibility (only meaningful while filtering is active).
|
|
@@ -2176,7 +2135,7 @@ export class Renderer {
|
|
|
2176
2135
|
// Frustum culling: skip batches entirely outside the camera view
|
|
2177
2136
|
if (batch.bounds) {
|
|
2178
2137
|
const batchAABB = { min: batch.bounds.min, max: batch.bounds.max };
|
|
2179
|
-
if (!
|
|
2138
|
+
if (!isRteAabbVisible(frustum, batchAABB, rteCullCamera)) {
|
|
2180
2139
|
frameBatchesFrustumCulled++;
|
|
2181
2140
|
continue; // Entire batch is off-screen
|
|
2182
2141
|
}
|
|
@@ -2272,6 +2231,8 @@ export class Renderer {
|
|
|
2272
2231
|
const tpl = this.uniformScratch;
|
|
2273
2232
|
const tplFlags = this.uniformScratchU32;
|
|
2274
2233
|
tpl.set(viewProj, 0);
|
|
2234
|
+
relativeToEyeFrame.packUniforms(tpl, MESH_UNIFORM_OFFSET.rteViewProj);
|
|
2235
|
+
packRteCameraOrigin(relativeToEyeFrame, tpl);
|
|
2275
2236
|
// Identity model matrix (positions already in world space)
|
|
2276
2237
|
tpl[16] = 1;
|
|
2277
2238
|
tpl[17] = 0;
|
|
@@ -2289,12 +2250,9 @@ export class Renderer {
|
|
|
2289
2250
|
tpl[29] = 0;
|
|
2290
2251
|
tpl[30] = 0;
|
|
2291
2252
|
tpl[31] = 1;
|
|
2292
|
-
//
|
|
2293
|
-
//
|
|
2294
|
-
tpl
|
|
2295
|
-
tpl[37] = 0.6; // roughness
|
|
2296
|
-
tpl[38] = 0;
|
|
2297
|
-
tpl[39] = 0; // padding
|
|
2253
|
+
// Colour + material row (tpl[32..39]) are patched per draw; the
|
|
2254
|
+
// instanced passes keep this opaque default.
|
|
2255
|
+
packMeshMaterial(tpl);
|
|
2298
2256
|
if (sectionPlaneData) {
|
|
2299
2257
|
tpl[40] = sectionPlaneData.normal[0];
|
|
2300
2258
|
tpl[41] = sectionPlaneData.normal[1];
|
|
@@ -2322,15 +2280,22 @@ export class Renderer {
|
|
|
2322
2280
|
tplClipBit;
|
|
2323
2281
|
tplFlags[2] = edgeEnabledU32;
|
|
2324
2282
|
tplFlags[3] = edgeIntensityMilliU32;
|
|
2283
|
+
// Flat/quantized/textured batches enter WGSL in the single
|
|
2284
|
+
// camera-relative frame. Their fragment clip inputs must use
|
|
2285
|
+
// that same frame; mixing a local vertex with a 5,000 km f32
|
|
2286
|
+
// world plane loses centimetre cuts before the comparison.
|
|
2287
|
+
packRteFragmentSpace(relativeToEyeFrame, sectionPlaneData, options.clipBox, tpl);
|
|
2325
2288
|
// Helper function to render a batch — patches color into the shared template
|
|
2326
2289
|
const renderBatch = (batch) => {
|
|
2327
2290
|
if (!batch.bindGroup || !batch.uniformBuffer)
|
|
2328
2291
|
return;
|
|
2329
|
-
// Patch
|
|
2292
|
+
// Patch the per-batch colour, and the material its AUTHORED
|
|
2293
|
+
// alpha implies: an X-Ray fade is not glass (#5386).
|
|
2330
2294
|
tpl[32] = batch.color[0];
|
|
2331
2295
|
tpl[33] = batch.color[1];
|
|
2332
2296
|
tpl[34] = batch.color[2];
|
|
2333
2297
|
tpl[35] = alphaForBatch(batch, batch.color[3]);
|
|
2298
|
+
packMeshMaterial(tpl, batch.color[3]);
|
|
2334
2299
|
// Per-batch local frame: the batch's vertices are stored
|
|
2335
2300
|
// RELATIVE to batch.origin (f32-small), so set the model
|
|
2336
2301
|
// matrix translation column to origin (world = origin + pos).
|
|
@@ -2341,6 +2306,11 @@ export class Renderer {
|
|
|
2341
2306
|
tpl[28] = o ? o[0] : 0;
|
|
2342
2307
|
tpl[29] = o ? o[1] : 0;
|
|
2343
2308
|
tpl[30] = o ? o[2] : 0;
|
|
2309
|
+
// The regular model matrix remains for absolute-space
|
|
2310
|
+
// fragment work (section/shadow); vertex projection uses
|
|
2311
|
+
// this f64-subtracted high/low origin instead.
|
|
2312
|
+
relativeToEyeFrame.packDrawableOrigin(o ?? [0, 0, 0], tpl, MESH_UNIFORM_OFFSET.drawableDelta);
|
|
2313
|
+
tplFlags[0] |= MESH_FLAG_RTE_DRAWABLE;
|
|
2344
2314
|
// Quantized dequantization params (issue #1682 phase 6);
|
|
2345
2315
|
// zeroed for f32 batches (their pipelines ignore them).
|
|
2346
2316
|
const qz = batch.quantized;
|
|
@@ -2420,7 +2390,7 @@ export class Renderer {
|
|
|
2420
2390
|
const kept = [];
|
|
2421
2391
|
for (const it of instancedTemplates) {
|
|
2422
2392
|
if (it.bounds) {
|
|
2423
|
-
if (!
|
|
2393
|
+
if (!isRteAabbVisible(frustum, it.bounds, rteCullCamera)) {
|
|
2424
2394
|
frameInstancedFrustumCulled++;
|
|
2425
2395
|
continue;
|
|
2426
2396
|
}
|
|
@@ -2438,11 +2408,12 @@ export class Renderer {
|
|
|
2438
2408
|
visibleInstanced = kept;
|
|
2439
2409
|
}
|
|
2440
2410
|
if (visibleInstanced.length > 0) {
|
|
2411
|
+
uploadInstancedRteDeltas(device, visibleInstanced, relativeToEyeFrame.getCameraWorld());
|
|
2441
2412
|
// Opaque instanced pass. flags.x bit 2 marks "instanced pass" so the
|
|
2442
2413
|
// shader routes per-instance opacity: opaque (or selected) occurrences
|
|
2443
2414
|
// draw here; translucent ones (lens/x-ray/compare overrides) are
|
|
2444
2415
|
// discarded and drawn in the transparent sub-pass below.
|
|
2445
|
-
this.pipeline.writeRawUniforms(tpl, 0x4);
|
|
2416
|
+
this.pipeline.writeRawUniforms(tpl, MESH_FLAG_RTE_DRAWABLE | 0x4);
|
|
2446
2417
|
pass.setPipeline(this.pipeline.getInstancedPipeline());
|
|
2447
2418
|
pass.setBindGroup(0, this.pipeline.getBindGroup());
|
|
2448
2419
|
pass.setBindGroup(1, this.pipeline.getEnvironmentBindGroup());
|
|
@@ -2502,10 +2473,22 @@ export class Renderer {
|
|
|
2502
2473
|
tpl[28] = tm.origin[0];
|
|
2503
2474
|
tpl[29] = tm.origin[1];
|
|
2504
2475
|
tpl[30] = tm.origin[2];
|
|
2476
|
+
relativeToEyeFrame.packDrawableOrigin(tm.origin, tpl, MESH_UNIFORM_OFFSET.drawableDelta);
|
|
2477
|
+
tplFlags[0] |= MESH_FLAG_RTE_DRAWABLE;
|
|
2505
2478
|
tpl[32] = txOverride ? txOverride[0] : tm.color[0];
|
|
2506
2479
|
tpl[33] = txOverride ? txOverride[1] : tm.color[1];
|
|
2507
2480
|
tpl[34] = txOverride ? txOverride[2] : tm.color[2];
|
|
2508
2481
|
tpl[35] = txAlpha;
|
|
2482
|
+
// Same authored-alpha + material contract as the flat/batched
|
|
2483
|
+
// paths (mesh-material.ts) — a call with neither argument
|
|
2484
|
+
// silently gave every textured metal or translucent mesh the
|
|
2485
|
+
// opaque dielectric default (#5623 review). The pipeline
|
|
2486
|
+
// itself is still opaque/depth-write only (no blend state,
|
|
2487
|
+
// see `txAlpha` above), so a translucent authored alpha still
|
|
2488
|
+
// never actually blends here — only the glass/roughness
|
|
2489
|
+
// CHOICE in the shader follows the same rule as everywhere
|
|
2490
|
+
// else, for the same reason `tm.material` does.
|
|
2491
|
+
packMeshMaterial(tpl, tm.color[3], tm.material);
|
|
2509
2492
|
device.queue.writeBuffer(tm.uniformBuffer, 0, tpl);
|
|
2510
2493
|
pass.setBindGroup(0, tm.bindGroup);
|
|
2511
2494
|
pass.setVertexBuffer(0, tm.vertexBuffer);
|
|
@@ -2568,7 +2551,7 @@ export class Renderer {
|
|
|
2568
2551
|
// flags.x bit 1 = overlay: tells the shader to preserve baseColor.a
|
|
2569
2552
|
// (the overlay pipeline now has src-alpha blending so low-alpha ghost
|
|
2570
2553
|
// tints composite correctly against the opaque pass) AND skip the
|
|
2571
|
-
//
|
|
2554
|
+
// specular term (glass reflections are meant for real glass and
|
|
2572
2555
|
// would whiten low-alpha colour overrides at grazing angles).
|
|
2573
2556
|
const overrideBatches = this.scene.getOverrideBatches();
|
|
2574
2557
|
if (overrideBatches.length > 0) {
|
|
@@ -2648,7 +2631,7 @@ export class Renderer {
|
|
|
2648
2631
|
if (visibleInstanced.length > 0 &&
|
|
2649
2632
|
this.scene.hasTransparentInstances() &&
|
|
2650
2633
|
instancedTransparentPipeline !== null) {
|
|
2651
|
-
this.pipeline.writeRawUniforms(tpl, 0x4 | 0x8);
|
|
2634
|
+
this.pipeline.writeRawUniforms(tpl, MESH_FLAG_RTE_DRAWABLE | 0x4 | 0x8);
|
|
2652
2635
|
pass.setPipeline(instancedTransparentPipeline);
|
|
2653
2636
|
pass.setBindGroup(0, this.pipeline.getBindGroup());
|
|
2654
2637
|
pass.setBindGroup(1, this.pipeline.getEnvironmentBindGroup());
|
|
@@ -2682,10 +2665,7 @@ export class Renderer {
|
|
|
2682
2665
|
tpl[33] = mesh.color[1];
|
|
2683
2666
|
tpl[34] = mesh.color[2];
|
|
2684
2667
|
tpl[35] = alphaForMesh(mesh.expressId, mesh.color[3]);
|
|
2685
|
-
tpl[
|
|
2686
|
-
tpl[37] = mesh.material?.roughness ?? 0.6;
|
|
2687
|
-
tpl[38] = 0;
|
|
2688
|
-
tpl[39] = 0;
|
|
2668
|
+
packMeshMaterial(tpl, mesh.color[3], mesh.material);
|
|
2689
2669
|
if (sectionPlaneData) {
|
|
2690
2670
|
tpl[40] = sectionPlaneData.normal[0];
|
|
2691
2671
|
tpl[41] = sectionPlaneData.normal[1];
|
|
@@ -2705,6 +2685,10 @@ export class Renderer {
|
|
|
2705
2685
|
tplClipBit;
|
|
2706
2686
|
tplFlags[2] = edgeEnabledU32;
|
|
2707
2687
|
tplFlags[3] = edgeIntensityMilliU32;
|
|
2688
|
+
packRteFragmentSpace(relativeToEyeFrame, sectionPlaneData, options.clipBox, tpl);
|
|
2689
|
+
const transparentOrigin = mesh.rteOrigin ?? [mesh.transform.m[12], mesh.transform.m[13], mesh.transform.m[14]];
|
|
2690
|
+
relativeToEyeFrame.packDrawableOrigin(transparentOrigin, tpl, MESH_UNIFORM_OFFSET.drawableDelta);
|
|
2691
|
+
tplFlags[0] |= MESH_FLAG_RTE_DRAWABLE;
|
|
2708
2692
|
device.queue.writeBuffer(mesh.uniformBuffer, 0, tpl);
|
|
2709
2693
|
pass.setBindGroup(0, mesh.bindGroup);
|
|
2710
2694
|
pass.setVertexBuffer(0, mesh.vertexBuffer);
|
|
@@ -2742,10 +2726,7 @@ export class Renderer {
|
|
|
2742
2726
|
tpl[33] = mesh.color[1];
|
|
2743
2727
|
tpl[34] = mesh.color[2];
|
|
2744
2728
|
tpl[35] = mesh.color[3];
|
|
2745
|
-
tpl[
|
|
2746
|
-
tpl[37] = mesh.material?.roughness ?? 0.6;
|
|
2747
|
-
tpl[38] = 0;
|
|
2748
|
-
tpl[39] = 0;
|
|
2729
|
+
packMeshMaterial(tpl, mesh.color[3], mesh.material);
|
|
2749
2730
|
if (sectionPlaneData) {
|
|
2750
2731
|
tpl[40] = sectionPlaneData.normal[0];
|
|
2751
2732
|
tpl[41] = sectionPlaneData.normal[1];
|
|
@@ -2765,6 +2746,10 @@ export class Renderer {
|
|
|
2765
2746
|
tplClipBit;
|
|
2766
2747
|
tplFlags[2] = edgeEnabledU32;
|
|
2767
2748
|
tplFlags[3] = edgeIntensityMilliU32;
|
|
2749
|
+
packRteFragmentSpace(relativeToEyeFrame, sectionPlaneData, options.clipBox, tpl);
|
|
2750
|
+
const selectedOrigin = mesh.rteOrigin ?? [mesh.transform.m[12], mesh.transform.m[13], mesh.transform.m[14]];
|
|
2751
|
+
relativeToEyeFrame.packDrawableOrigin(selectedOrigin, tpl, MESH_UNIFORM_OFFSET.drawableDelta);
|
|
2752
|
+
tplFlags[0] |= MESH_FLAG_RTE_DRAWABLE;
|
|
2768
2753
|
device.queue.writeBuffer(mesh.uniformBuffer, 0, tpl);
|
|
2769
2754
|
pass.setPipeline(this.pipeline.getSelectionPipeline());
|
|
2770
2755
|
pass.setBindGroup(0, mesh.bindGroup);
|
|
@@ -2812,65 +2797,55 @@ export class Renderer {
|
|
|
2812
2797
|
// viewport size to convert pixel sizes into clip-space offsets.
|
|
2813
2798
|
if (this.pointCloudRenderer && this.pointCloudRenderer.hasAssets()) {
|
|
2814
2799
|
this.pointCloudRenderer.draw(pass, {
|
|
2815
|
-
viewProj,
|
|
2800
|
+
viewProj: relativeToEyeFrame.getViewProjection().m,
|
|
2801
|
+
relativeToEyeFrame,
|
|
2816
2802
|
sectionPlane: sectionPlaneData
|
|
2817
2803
|
? { ...sectionPlaneData, flipped: options.sectionPlane?.flipped === true }
|
|
2818
2804
|
: null,
|
|
2819
|
-
|
|
2805
|
+
clipBox: options.clipBox,
|
|
2806
|
+
viewport: { width: this.canvas.width / this.pixelRatio, height: this.canvas.height / this.pixelRatio },
|
|
2820
2807
|
});
|
|
2821
2808
|
}
|
|
2822
2809
|
// Section-plane gizmo, 2D section cap and every standalone 3D
|
|
2823
2810
|
// overlay (annotation / alignment / grid / DXF / clash / symbolic
|
|
2824
2811
|
// text). One draw call into the pass — see RendererOverlays.draw().
|
|
2825
|
-
this.referenceImages.draw(pass, viewProj);
|
|
2812
|
+
this.referenceImages.draw(pass, viewProj, relativeToEyeFrame.getViewProjection().m, relativeToEyeFrame.getCameraWorld());
|
|
2826
2813
|
this.overlays.draw(pass, {
|
|
2827
2814
|
options,
|
|
2828
2815
|
viewProj,
|
|
2829
2816
|
modelBounds: this.getModelBounds(),
|
|
2830
2817
|
camera: this.camera,
|
|
2831
|
-
canvasWidth: this.canvas.width,
|
|
2832
|
-
canvasHeight: this.canvas.height,
|
|
2818
|
+
canvasWidth: this.canvas.width / this.pixelRatio, // CSS px: glyph sizes are CSS px
|
|
2819
|
+
canvasHeight: this.canvas.height / this.pixelRatio, pixelRatio: this.pixelRatio,
|
|
2820
|
+
relativeToEyeFrame,
|
|
2821
|
+
rteViewProj: relativeToEyeFrame.getViewProjection().m,
|
|
2822
|
+
rteCamera: relativeToEyeFrame.getCameraWorld(),
|
|
2833
2823
|
});
|
|
2834
2824
|
pass.end();
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
this.
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
separationRadius: Math.min(2.0, Math.max(1.0, visualEnhancement.separationLines.radius)),
|
|
2854
|
-
separationIntensity: separationEnabled ? Math.min(1.0, Math.max(0.0, visualEnhancement.separationLines.intensity)) : 0.0,
|
|
2855
|
-
enableSeparationLines: separationEnabled,
|
|
2856
|
-
});
|
|
2857
|
-
}
|
|
2858
|
-
// Eye-Dome Lighting — runs AFTER contact/separation so it darkens
|
|
2859
|
-
// every layer uniformly. Cheap (~9 depth taps), only active when
|
|
2860
|
-
// there are point clouds in the scene and the user has enabled it.
|
|
2861
|
-
if (this.edlPass
|
|
2862
|
-
&& this.edlOptions.enabled
|
|
2863
|
-
&& this.pointCloudRenderer?.hasAssets()) {
|
|
2864
|
-
this.edlPass.apply(encoder, {
|
|
2865
|
-
targetView: textureView,
|
|
2866
|
-
depthView: this.pipeline.getDepthOnlyTextureView(),
|
|
2867
|
-
}, {
|
|
2868
|
-
strength: this.edlOptions.strength,
|
|
2869
|
-
radiusPx: this.edlOptions.radiusPx,
|
|
2870
|
-
highQuality: this.edlOptions.highQuality,
|
|
2871
|
-
});
|
|
2872
|
-
}
|
|
2825
|
+
// Created lazily like the sky/shadow passes; each pass inside is too.
|
|
2826
|
+
this.postPasses ??= new PostPassChain(this.device, this.pipeline.getSampleCount());
|
|
2827
|
+
this.postPasses.encode({
|
|
2828
|
+
encoder,
|
|
2829
|
+
targetView: textureView,
|
|
2830
|
+
width: this.canvas.width,
|
|
2831
|
+
height: this.canvas.height,
|
|
2832
|
+
// Depth-only: depth24plus-stencil8 cannot be sampled as texture_depth_* with aspect 'all'.
|
|
2833
|
+
depthView: this.pipeline.getDepthOnlyTextureView(),
|
|
2834
|
+
objectIdView,
|
|
2835
|
+
projection: this.camera.getProjMatrix(),
|
|
2836
|
+
enhancement: visualEnhancement,
|
|
2837
|
+
effectsLive,
|
|
2838
|
+
pixelRatio: this.pixelRatio,
|
|
2839
|
+
// EDL only when point clouds are loaded and the user enabled it.
|
|
2840
|
+
edl: this.edlOptions.enabled && this.pointCloudRenderer?.hasAssets() ? this.edlOptions : null,
|
|
2841
|
+
});
|
|
2842
|
+
colorReadback = colorCapture && encodeRendererColorFrameCapture(device, encoder, colorCapture);
|
|
2873
2843
|
device.queue.submit([encoder.finish()]);
|
|
2844
|
+
if (colorReadback && colorCapture) {
|
|
2845
|
+
settleRendererColorFrameCapture(this, colorCapture, colorReadback);
|
|
2846
|
+
colorCapture = null;
|
|
2847
|
+
colorReadback = null;
|
|
2848
|
+
}
|
|
2874
2849
|
this._lastFrameStats = {
|
|
2875
2850
|
drawCalls: frameDrawCalls,
|
|
2876
2851
|
batchesDrawn: frameBatchesDrawn,
|
|
@@ -2894,6 +2869,7 @@ export class Renderer {
|
|
|
2894
2869
|
}
|
|
2895
2870
|
}
|
|
2896
2871
|
catch (error) {
|
|
2872
|
+
discardRendererColorFrameReadback(colorReadback);
|
|
2897
2873
|
// Balance the validation scope if we threw before popping it above —
|
|
2898
2874
|
// an unpopped scope would capture every later frame's errors silently.
|
|
2899
2875
|
// drainErrorScope logs a pop rejection (device loss) rather than
|
|
@@ -2913,7 +2889,9 @@ export class Renderer {
|
|
|
2913
2889
|
// typed-array views — plus one 5-argument call in
|
|
2914
2890
|
// `point-cloud-uniforms.ts` whose offset and size are compile-time
|
|
2915
2891
|
// constants matching its scratch array — so the spec's
|
|
2916
|
-
// `OperationError` preconditions are unreachable
|
|
2892
|
+
// `OperationError` preconditions are unreachable (the partial-batch
|
|
2893
|
+
// upload's `createStaticGpuBuffer` also pads its whole-view payload
|
|
2894
|
+
// to the 4-byte multiple the spec demands, #5429); the one
|
|
2917
2895
|
// `copyExternalImageToTexture` copies the glyph atlas's own
|
|
2918
2896
|
// never-externally-drawn canvas at its full fixed size, so neither
|
|
2919
2897
|
// `SecurityError` nor a zero-size `OperationError` can arise; and
|
|
@@ -2922,10 +2900,11 @@ export class Renderer {
|
|
|
2922
2900
|
// draws, `finish`, `submit`, `createBindGroup`) reports failure as
|
|
2923
2901
|
// an asynchronous `GPUValidationError` through the error scope, not
|
|
2924
2902
|
// as a throw. The region's real healthy-device failure is
|
|
2925
|
-
// `getOrCreatePartialBatch`'s
|
|
2926
|
-
//
|
|
2903
|
+
// `getOrCreatePartialBatch`'s CPU-side geometry merge running out of
|
|
2904
|
+
// host memory, and that throws a `RangeError` — which is exactly why
|
|
2927
2905
|
// the discriminator keys on the TYPE and not on "a frame threw".
|
|
2928
2906
|
this.containFrameThrow(error, 'encode');
|
|
2907
|
+
cancelRendererColorFrame(this);
|
|
2929
2908
|
}
|
|
2930
2909
|
}
|
|
2931
2910
|
/**
|
|
@@ -3106,13 +3085,21 @@ export class Renderer {
|
|
|
3106
3085
|
this.overlays.clearSection2DOverlay();
|
|
3107
3086
|
}
|
|
3108
3087
|
/**
|
|
3109
|
-
* Set the colour
|
|
3110
|
-
* section-
|
|
3111
|
-
*
|
|
3088
|
+
* Set the one colour vocabulary every overlay draws in (#5484): the selection
|
|
3089
|
+
* highlight, the section-plane preview (one accent for every axis), every
|
|
3090
|
+
* overlay line channel (section-cut outline, IfcAnnotation, alignment,
|
|
3091
|
+
* IfcGrid, DXF / LandXML) and the clash pair / overlap tints. Call this on
|
|
3092
|
+
* theme change and once after `init()`; the underlying GPU uniforms are
|
|
3093
|
+
* written only here, never per frame. Supersedes `setOverlayLineColor`
|
|
3094
|
+
* (deleted — the app never called it with a themed colour). See
|
|
3095
|
+
* `OverlayTheme` for which fields want linear-light RGBA and which want
|
|
3096
|
+
* sRGB-direct RGBA. The matching label colour is per-text via
|
|
3112
3097
|
* `SymbolicTextInput.color` on `uploadAnnotationTexts3D`.
|
|
3113
3098
|
*/
|
|
3114
|
-
|
|
3115
|
-
this.
|
|
3099
|
+
setOverlayTheme(theme) {
|
|
3100
|
+
this.overlayTheme = theme;
|
|
3101
|
+
this.pipeline?.selectionColorUniform.update(theme.selection);
|
|
3102
|
+
this.overlays.setTheme(theme);
|
|
3116
3103
|
}
|
|
3117
3104
|
/**
|
|
3118
3105
|
* Set one standalone 3D line overlay, or clear it by passing `null`.
|
|
@@ -3125,7 +3112,7 @@ export class Renderer {
|
|
|
3125
3112
|
*
|
|
3126
3113
|
* Every channel is an independent buffer with its own visibility, so
|
|
3127
3114
|
* setting one leaves the other three untouched. All four share the colour
|
|
3128
|
-
* set by
|
|
3115
|
+
* set by `Renderer.setOverlayTheme`'s `overlayLine` field; label colour is per-text via
|
|
3129
3116
|
* `SymbolicTextInput.color` on `uploadAnnotationTexts3D`.
|
|
3130
3117
|
*
|
|
3131
3118
|
* The channels differ in exactly one way — whether they grow the scene
|
|
@@ -3145,8 +3132,10 @@ export class Renderer {
|
|
|
3145
3132
|
/**
|
|
3146
3133
|
* Show (or clear) the clash-overlap box: the wireframe AABB of a focused
|
|
3147
3134
|
* clash, drawn in `color` so the overlap region reads as a distinct third
|
|
3148
|
-
* colour next to the two glowing clash elements (#1277).
|
|
3149
|
-
*
|
|
3135
|
+
* colour next to the two glowing clash elements (#1277). Omit `color` to
|
|
3136
|
+
* draw it in the overlay theme's `clashOverlap` and keep following it
|
|
3137
|
+
* across `setOverlayTheme` calls (#5490). Pass `null` to clear.
|
|
3138
|
+
* `min`/`max` are world-space corners (clash works in world frame).
|
|
3150
3139
|
*/
|
|
3151
3140
|
setClashOverlapBox(box) {
|
|
3152
3141
|
this.overlays.setClashOverlapBox(box);
|
|
@@ -3155,8 +3144,10 @@ export class Renderer {
|
|
|
3155
3144
|
* Draw the focused clash's CONTACT geometry as 3D line segments — the real
|
|
3156
3145
|
* shared-face polygon outlines / intersection lines, not the AABB box.
|
|
3157
3146
|
* `vertices` is a flat line-list (x,y,z per endpoint, 2 endpoints per
|
|
3158
|
-
* segment) in world frame.
|
|
3159
|
-
*
|
|
3147
|
+
* segment) in world frame. Omit `color` to follow the overlay theme's
|
|
3148
|
+
* `clashOverlap`, as for `setClashOverlapBox`. Pass `null` to clear. Shares
|
|
3149
|
+
* the clash-box line buffer, so only one of this / setClashOverlapBox is
|
|
3150
|
+
* shown at a time.
|
|
3160
3151
|
*/
|
|
3161
3152
|
setClashContactLines(lines) {
|
|
3162
3153
|
this.overlays.setClashContactLines(lines);
|
|
@@ -3168,7 +3159,8 @@ export class Renderer {
|
|
|
3168
3159
|
* BIMcollab Zoom / Solibri presentation). Pass `null` to clear. Independent
|
|
3169
3160
|
* of `setClashOverlapBox` / `setClashContactLines`: the caller decides
|
|
3170
3161
|
* which one is current for a given clash (solid when the kernel resolved
|
|
3171
|
-
* one, box/lines as the fallback when it didn't).
|
|
3162
|
+
* one, box/lines as the fallback when it didn't). Omit `color` to follow
|
|
3163
|
+
* the overlay theme's `clashOverlap`, as for `setClashOverlapBox`.
|
|
3172
3164
|
*/
|
|
3173
3165
|
setClashIntersectionSolid(solid) {
|
|
3174
3166
|
this.overlays.setClashIntersectionSolid(solid);
|
|
@@ -3252,30 +3244,9 @@ export class Renderer {
|
|
|
3252
3244
|
}
|
|
3253
3245
|
return this.device.getDevice();
|
|
3254
3246
|
}
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
* @returns PNG data URL or null if capture failed
|
|
3259
|
-
*/
|
|
3260
|
-
async captureScreenshot() {
|
|
3261
|
-
if (!this.device.isInitialized()) {
|
|
3262
|
-
console.warn('[Renderer] Cannot capture screenshot: not initialized');
|
|
3263
|
-
return null;
|
|
3264
|
-
}
|
|
3265
|
-
try {
|
|
3266
|
-
// Wait for any pending GPU work to complete before capturing
|
|
3267
|
-
// This ensures we capture the fully rendered frame
|
|
3268
|
-
const device = this.device.getDevice();
|
|
3269
|
-
await device.queue.onSubmittedWorkDone();
|
|
3270
|
-
// Capture exactly what's displayed on the canvas
|
|
3271
|
-
const dataUrl = this.canvas.toDataURL('image/png');
|
|
3272
|
-
return dataUrl;
|
|
3273
|
-
}
|
|
3274
|
-
catch (error) {
|
|
3275
|
-
console.error('[Renderer] Screenshot capture failed:', error);
|
|
3276
|
-
return null;
|
|
3277
|
-
}
|
|
3278
|
-
}
|
|
3247
|
+
captureScreenshot() { return captureRendererScreenshot(this.device, this.canvas); }
|
|
3248
|
+
/** Capture the next submitted frame's bounded color pixels, not compositor state. */
|
|
3249
|
+
captureColorFrame() { return requestRendererColorFrame(this, !this.destroyed && !this.deviceLost && this.device.isInitialized(), () => this.requestRender()); }
|
|
3279
3250
|
/**
|
|
3280
3251
|
* Destroy the renderer and release all GPU resources.
|
|
3281
3252
|
*
|
|
@@ -3310,6 +3281,7 @@ export class Renderer {
|
|
|
3310
3281
|
destroy() {
|
|
3311
3282
|
this.initGeneration++;
|
|
3312
3283
|
this.destroyed = true;
|
|
3284
|
+
cancelRendererColorFrame(this);
|
|
3313
3285
|
this.teardown();
|
|
3314
3286
|
this.rejectReadyWaiters(rendererDestroyedError());
|
|
3315
3287
|
}
|
|
@@ -3323,6 +3295,7 @@ export class Renderer {
|
|
|
3323
3295
|
// go back to waiting. Set first: every release below is synchronous, but
|
|
3324
3296
|
// the flag is what a caller holding a live reference actually reads.
|
|
3325
3297
|
this.ready = false;
|
|
3298
|
+
cancelRendererColorFrame(this);
|
|
3326
3299
|
// Scene mesh GPU buffers
|
|
3327
3300
|
if (clearScene) {
|
|
3328
3301
|
this.scene.clear();
|
|
@@ -3339,11 +3312,8 @@ export class Renderer {
|
|
|
3339
3312
|
this.picker?.destroy();
|
|
3340
3313
|
this.picker = null;
|
|
3341
3314
|
this.pickingManager.setPicker(null);
|
|
3342
|
-
|
|
3343
|
-
this.
|
|
3344
|
-
this.postProcessor = null;
|
|
3345
|
-
this.edlPass?.destroy();
|
|
3346
|
-
this.edlPass = null;
|
|
3315
|
+
this.postPasses?.destroy();
|
|
3316
|
+
this.postPasses = null;
|
|
3347
3317
|
this.skyPass?.destroy();
|
|
3348
3318
|
this.skyPass = null;
|
|
3349
3319
|
this.shadowPass?.destroy();
|
|
@@ -3353,6 +3323,7 @@ export class Renderer {
|
|
|
3353
3323
|
this.overlays.destroy();
|
|
3354
3324
|
this.referenceImages.destroy();
|
|
3355
3325
|
// Point cloud GPU resources
|
|
3326
|
+
this.pointCloudNextHandleId = this.pointCloudRenderer?.handleIds?.current() ?? this.pointCloudNextHandleId;
|
|
3356
3327
|
this.pointCloudStreamEpoch++;
|
|
3357
3328
|
this.pointCloudStreamEpochs.clear();
|
|
3358
3329
|
this.pointCloudRenderer?.clear();
|