@ifc-lite/renderer 5.0.0 → 6.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.
- package/dist/camera-projection.d.ts +2 -0
- package/dist/camera-projection.d.ts.map +1 -1
- package/dist/camera-projection.js +46 -0
- package/dist/camera-projection.js.map +1 -1
- package/dist/chunk-grid.d.ts +21 -0
- package/dist/chunk-grid.d.ts.map +1 -1
- package/dist/chunk-grid.js +25 -7
- package/dist/chunk-grid.js.map +1 -1
- package/dist/clash-solid-pipeline.d.ts.map +1 -1
- package/dist/clash-solid-pipeline.js +4 -2
- package/dist/clash-solid-pipeline.js.map +1 -1
- package/dist/deviation/deviation-computer.d.ts +4 -0
- package/dist/deviation/deviation-computer.d.ts.map +1 -1
- package/dist/deviation/deviation-computer.js +20 -0
- package/dist/deviation/deviation-computer.js.map +1 -1
- package/dist/deviation/deviation-readback.d.ts +18 -0
- package/dist/deviation/deviation-readback.d.ts.map +1 -0
- package/dist/deviation/deviation-readback.js +67 -0
- package/dist/deviation/deviation-readback.js.map +1 -0
- package/dist/edge-pass.d.ts +16 -0
- package/dist/edge-pass.d.ts.map +1 -1
- package/dist/edge-pass.js +75 -1
- package/dist/edge-pass.js.map +1 -1
- package/dist/entity-color-table.d.ts +140 -0
- package/dist/entity-color-table.d.ts.map +1 -0
- package/dist/entity-color-table.js +238 -0
- package/dist/entity-color-table.js.map +1 -0
- package/dist/gpu-upload-guard.d.ts +1 -1
- package/dist/gpu-upload-guard.js +1 -1
- package/dist/hover-mesh-cache.d.ts +29 -0
- package/dist/hover-mesh-cache.d.ts.map +1 -0
- package/dist/hover-mesh-cache.js +31 -0
- package/dist/hover-mesh-cache.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +146 -161
- package/dist/index.js.map +1 -1
- package/dist/individual-mesh-upload.d.ts +24 -0
- package/dist/individual-mesh-upload.d.ts.map +1 -0
- package/dist/individual-mesh-upload.js +85 -0
- package/dist/individual-mesh-upload.js.map +1 -0
- package/dist/instanced-render.d.ts +19 -9
- package/dist/instanced-render.d.ts.map +1 -1
- package/dist/instanced-render.js +36 -24
- package/dist/instanced-render.js.map +1 -1
- package/dist/instanced-rte.d.ts +68 -7
- package/dist/instanced-rte.d.ts.map +1 -1
- package/dist/instanced-rte.js +98 -17
- package/dist/instanced-rte.js.map +1 -1
- package/dist/instanced-vertex-layout.d.ts +26 -0
- package/dist/instanced-vertex-layout.d.ts.map +1 -0
- package/dist/instanced-vertex-layout.js +61 -0
- package/dist/instanced-vertex-layout.js.map +1 -0
- package/dist/mesh-rte-uniforms.d.ts +4 -2
- package/dist/mesh-rte-uniforms.d.ts.map +1 -1
- package/dist/mesh-rte-uniforms.js +4 -2
- package/dist/mesh-rte-uniforms.js.map +1 -1
- package/dist/model-translation.d.ts.map +1 -1
- package/dist/model-translation.js +0 -7
- package/dist/model-translation.js.map +1 -1
- package/dist/outline-params.d.ts +45 -0
- package/dist/outline-params.d.ts.map +1 -0
- package/dist/outline-params.js +54 -0
- package/dist/outline-params.js.map +1 -0
- package/dist/overlay-routing.d.ts +8 -8
- package/dist/overlay-routing.js +2 -2
- package/dist/overlay-routing.js.map +1 -1
- package/dist/overlay-theme.d.ts +9 -3
- package/dist/overlay-theme.d.ts.map +1 -1
- package/dist/overlay-theme.js.map +1 -1
- package/dist/partial-batch-requests.d.ts +2 -2
- package/dist/pick-mesh-budget.d.ts +28 -0
- package/dist/pick-mesh-budget.d.ts.map +1 -0
- package/dist/pick-mesh-budget.js +74 -0
- package/dist/pick-mesh-budget.js.map +1 -0
- package/dist/picker-rte-uniforms.d.ts +7 -1
- package/dist/picker-rte-uniforms.d.ts.map +1 -1
- package/dist/picker-rte-uniforms.js +9 -1
- package/dist/picker-rte-uniforms.js.map +1 -1
- package/dist/picker.d.ts.map +1 -1
- package/dist/picker.js +15 -28
- package/dist/picker.js.map +1 -1
- package/dist/picking-manager.d.ts.map +1 -1
- package/dist/picking-manager.js +4 -47
- package/dist/picking-manager.js.map +1 -1
- package/dist/pipeline.d.ts +7 -1
- package/dist/pipeline.d.ts.map +1 -1
- package/dist/pipeline.js +37 -59
- package/dist/pipeline.js.map +1 -1
- package/dist/point-picker.d.ts.map +1 -1
- package/dist/point-picker.js +7 -2
- package/dist/point-picker.js.map +1 -1
- package/dist/pointcloud/point-cloud-node.js +1 -1
- package/dist/pointcloud/point-cloud-node.js.map +1 -1
- package/dist/pointcloud/point-cloud-renderer.d.ts.map +1 -1
- package/dist/pointcloud/point-cloud-renderer.js +3 -1
- package/dist/pointcloud/point-cloud-renderer.js.map +1 -1
- package/dist/pointcloud/point-cloud-uniforms.d.ts +5 -1
- package/dist/pointcloud/point-cloud-uniforms.d.ts.map +1 -1
- package/dist/pointcloud/point-cloud-uniforms.js +7 -1
- package/dist/pointcloud/point-cloud-uniforms.js.map +1 -1
- package/dist/post-pass-chain.d.ts +14 -1
- package/dist/post-pass-chain.d.ts.map +1 -1
- package/dist/post-pass-chain.js +31 -2
- package/dist/post-pass-chain.js.map +1 -1
- package/dist/raycast-engine.d.ts +5 -1
- package/dist/raycast-engine.d.ts.map +1 -1
- package/dist/raycast-engine.js +20 -8
- package/dist/raycast-engine.js.map +1 -1
- package/dist/raycast-point-cloud-query.d.ts +2 -0
- package/dist/raycast-point-cloud-query.d.ts.map +1 -1
- package/dist/raycast-point-cloud-query.js +7 -0
- package/dist/raycast-point-cloud-query.js.map +1 -1
- package/dist/raycast-source-curve-query.d.ts +13 -0
- package/dist/raycast-source-curve-query.d.ts.map +1 -0
- package/dist/raycast-source-curve-query.js +17 -0
- package/dist/raycast-source-curve-query.js.map +1 -0
- package/dist/reference-image-pipeline.d.ts.map +1 -1
- package/dist/reference-image-pipeline.js +4 -2
- package/dist/reference-image-pipeline.js.map +1 -1
- package/dist/relative-to-eye-abi.d.ts +36 -0
- package/dist/relative-to-eye-abi.d.ts.map +1 -0
- package/dist/relative-to-eye-abi.js +72 -0
- package/dist/relative-to-eye-abi.js.map +1 -0
- package/dist/relative-to-eye.d.ts +15 -35
- package/dist/relative-to-eye.d.ts.map +1 -1
- package/dist/relative-to-eye.js +46 -74
- package/dist/relative-to-eye.js.map +1 -1
- package/dist/render-stats.d.ts +5 -1
- package/dist/render-stats.d.ts.map +1 -1
- package/dist/render-stats.js +1 -1
- package/dist/render-stats.js.map +1 -1
- package/dist/renderer-overlays.d.ts.map +1 -1
- package/dist/renderer-overlays.js +3 -2
- package/dist/renderer-overlays.js.map +1 -1
- package/dist/scene-batch-rebuild.d.ts +1 -2
- package/dist/scene-batch-rebuild.d.ts.map +1 -1
- package/dist/scene-batch-rebuild.js +4 -8
- package/dist/scene-batch-rebuild.js.map +1 -1
- package/dist/scene-batch-upload.d.ts.map +1 -1
- package/dist/scene-batch-upload.js +7 -3
- package/dist/scene-batch-upload.js.map +1 -1
- package/dist/scene-contents.d.ts +1 -1
- package/dist/scene-contents.d.ts.map +1 -1
- package/dist/scene-derived-batches.d.ts +19 -64
- package/dist/scene-derived-batches.d.ts.map +1 -1
- package/dist/scene-derived-batches.js +16 -51
- package/dist/scene-derived-batches.js.map +1 -1
- package/dist/scene-device-recovery.d.ts +3 -3
- package/dist/scene-device-recovery.d.ts.map +1 -1
- package/dist/scene-device-recovery.js +12 -9
- package/dist/scene-device-recovery.js.map +1 -1
- package/dist/scene-geometry.d.ts +6 -6
- package/dist/scene-geometry.d.ts.map +1 -1
- package/dist/scene-geometry.js +8 -9
- package/dist/scene-geometry.js.map +1 -1
- package/dist/scene-instance-bounds.d.ts +1 -1
- package/dist/scene-instance-bounds.d.ts.map +1 -1
- package/dist/scene-instance-bounds.js +4 -16
- package/dist/scene-instance-bounds.js.map +1 -1
- package/dist/scene-instance-materialization.d.ts.map +1 -1
- package/dist/scene-instance-materialization.js +4 -4
- package/dist/scene-instance-materialization.js.map +1 -1
- package/dist/scene-instance-translation.d.ts +6 -10
- package/dist/scene-instance-translation.d.ts.map +1 -1
- package/dist/scene-instance-translation.js +9 -31
- package/dist/scene-instance-translation.js.map +1 -1
- package/dist/scene-instance-types.d.ts +11 -2
- package/dist/scene-instance-types.d.ts.map +1 -1
- package/dist/scene-instance-types.js +7 -1
- package/dist/scene-instance-types.js.map +1 -1
- package/dist/scene-model-translation.d.ts +1 -2
- package/dist/scene-model-translation.d.ts.map +1 -1
- package/dist/scene-model-translation.js +4 -3
- package/dist/scene-model-translation.js.map +1 -1
- package/dist/scene-precision.d.ts +1 -1
- package/dist/scene-precision.d.ts.map +1 -1
- package/dist/scene-precision.js +11 -2
- package/dist/scene-precision.js.map +1 -1
- package/dist/scene.d.ts +43 -38
- package/dist/scene.d.ts.map +1 -1
- package/dist/scene.js +121 -129
- package/dist/scene.js.map +1 -1
- package/dist/section-2d-line-buffer.d.ts +2 -2
- package/dist/section-2d-line-buffer.d.ts.map +1 -1
- package/dist/section-2d-line-buffer.js +10 -11
- package/dist/section-2d-line-buffer.js.map +1 -1
- package/dist/section-2d-overlay.d.ts +6 -4
- package/dist/section-2d-overlay.d.ts.map +1 -1
- package/dist/section-2d-overlay.js +45 -21
- package/dist/section-2d-overlay.js.map +1 -1
- package/dist/section-2d-types.d.ts +1 -1
- package/dist/section-2d-types.d.ts.map +1 -1
- package/dist/section-2d-types.js +1 -1
- package/dist/section-2d-types.js.map +1 -1
- package/dist/section-plane.d.ts.map +1 -1
- package/dist/section-plane.js +3 -1
- package/dist/section-plane.js.map +1 -1
- package/dist/selection-mask-pass.d.ts +85 -0
- package/dist/selection-mask-pass.d.ts.map +1 -0
- package/dist/selection-mask-pass.js +170 -0
- package/dist/selection-mask-pass.js.map +1 -0
- package/dist/selection-mask-pipelines.d.ts +57 -0
- package/dist/selection-mask-pipelines.d.ts.map +1 -0
- package/dist/selection-mask-pipelines.js +150 -0
- package/dist/selection-mask-pipelines.js.map +1 -0
- package/dist/selection-outline-frame.d.ts +47 -0
- package/dist/selection-outline-frame.d.ts.map +1 -0
- package/dist/selection-outline-frame.js +122 -0
- package/dist/selection-outline-frame.js.map +1 -0
- package/dist/shaders/depth-reconstruct.wgsl.d.ts +7 -2
- package/dist/shaders/depth-reconstruct.wgsl.d.ts.map +1 -1
- package/dist/shaders/depth-reconstruct.wgsl.js +8 -3
- package/dist/shaders/depth-reconstruct.wgsl.js.map +1 -1
- package/dist/shaders/edges.wgsl.d.ts +18 -0
- package/dist/shaders/edges.wgsl.d.ts.map +1 -1
- package/dist/shaders/edges.wgsl.js +99 -0
- package/dist/shaders/edges.wgsl.js.map +1 -1
- package/dist/shaders/entity-color-table.wgsl.d.ts +2 -0
- package/dist/shaders/entity-color-table.wgsl.d.ts.map +1 -0
- package/dist/shaders/entity-color-table.wgsl.js +85 -0
- package/dist/shaders/entity-color-table.wgsl.js.map +1 -0
- package/dist/shaders/main.wgsl.d.ts +1 -1
- package/dist/shaders/main.wgsl.d.ts.map +1 -1
- package/dist/shaders/main.wgsl.js +45 -113
- package/dist/shaders/main.wgsl.js.map +1 -1
- package/dist/shaders/mesh-uniforms.wgsl.d.ts +2 -0
- package/dist/shaders/mesh-uniforms.wgsl.d.ts.map +1 -0
- package/dist/shaders/mesh-uniforms.wgsl.js +53 -0
- package/dist/shaders/mesh-uniforms.wgsl.js.map +1 -0
- package/dist/shaders/section-2d-overlay.wgsl.d.ts +1 -0
- package/dist/shaders/section-2d-overlay.wgsl.d.ts.map +1 -1
- package/dist/shaders/section-2d-overlay.wgsl.js +3 -2
- package/dist/shaders/section-2d-overlay.wgsl.js.map +1 -1
- package/dist/shaders/selection-mask.wgsl.d.ts +16 -0
- package/dist/shaders/selection-mask.wgsl.d.ts.map +1 -0
- package/dist/shaders/selection-mask.wgsl.js +138 -0
- package/dist/shaders/selection-mask.wgsl.js.map +1 -0
- 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 +3 -3
- package/dist/shadow-occluders.d.ts.map +1 -1
- package/dist/shadow-occluders.js +1 -0
- package/dist/shadow-occluders.js.map +1 -1
- package/dist/shadow-pass.d.ts +0 -1
- package/dist/shadow-pass.d.ts.map +1 -1
- package/dist/shadow-pass.js +26 -32
- package/dist/shadow-pass.js.map +1 -1
- package/dist/shadow-types.d.ts +3 -0
- package/dist/shadow-types.d.ts.map +1 -1
- package/dist/shadow-types.js.map +1 -1
- package/dist/snap-detector.d.ts +5 -0
- package/dist/snap-detector.d.ts.map +1 -1
- package/dist/snap-detector.js.map +1 -1
- package/dist/source-curve-clipping.d.ts +7 -0
- package/dist/source-curve-clipping.d.ts.map +1 -0
- package/dist/source-curve-clipping.js +165 -0
- package/dist/source-curve-clipping.js.map +1 -0
- package/dist/source-curve-snap.d.ts +49 -0
- package/dist/source-curve-snap.d.ts.map +1 -0
- package/dist/source-curve-snap.js +197 -0
- package/dist/source-curve-snap.js.map +1 -0
- package/dist/symbolic-overlay-pipelines.d.ts +1 -1
- package/dist/symbolic-overlay-pipelines.d.ts.map +1 -1
- package/dist/symbolic-overlay-pipelines.js +13 -22
- package/dist/symbolic-overlay-pipelines.js.map +1 -1
- package/dist/symbolic-text-rte.d.ts +16 -0
- package/dist/symbolic-text-rte.d.ts.map +1 -0
- package/dist/symbolic-text-rte.js +37 -0
- package/dist/symbolic-text-rte.js.map +1 -0
- package/dist/textured-vertices.d.ts.map +1 -1
- package/dist/textured-vertices.js +2 -3
- package/dist/textured-vertices.js.map +1 -1
- package/dist/types.d.ts +10 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/visual-enhancement.d.ts +0 -4
- package/dist/visual-enhancement.d.ts.map +1 -1
- package/dist/visual-enhancement.js +0 -5
- package/dist/visual-enhancement.js.map +1 -1
- package/dist/xray-alpha.d.ts +7 -3
- package/dist/xray-alpha.d.ts.map +1 -1
- package/dist/xray-alpha.js +11 -7
- package/dist/xray-alpha.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -78,9 +78,10 @@ import { WebGPUDevice } from './device.js';
|
|
|
78
78
|
import { RenderPipeline } from './pipeline.js';
|
|
79
79
|
import { Camera } from './camera.js';
|
|
80
80
|
import { Scene } from './scene.js';
|
|
81
|
+
import { remapOverrideColors } from './scene-derived-batches.js';
|
|
81
82
|
import { Picker } from './picker.js';
|
|
82
83
|
import { reportableItemId } from './pick-resolve.js';
|
|
83
|
-
import {
|
|
84
|
+
import { viewBasis } from './math.js';
|
|
84
85
|
import { isRteAabbVisible, rteFrustum, sourceFrustumFromRte } from './rte-frustum.js';
|
|
85
86
|
import { VisualEnhancementResolver, livePostEffects } from './visual-enhancement.js';
|
|
86
87
|
import { packClipBox } from './clip-box.js';
|
|
@@ -92,6 +93,8 @@ import { PickingManager } from './picking-manager.js';
|
|
|
92
93
|
import { RaycastEngine } from './raycast-engine.js';
|
|
93
94
|
import { RenderDegradationMonitor } from './render-degradation.js';
|
|
94
95
|
import { PostPassChain } from './post-pass-chain.js';
|
|
96
|
+
import { buildSelectionOutlineFrame, matchesHoveredMesh } from './selection-outline-frame.js';
|
|
97
|
+
import { HoverMeshCache } from './hover-mesh-cache.js';
|
|
95
98
|
import { InteractionEffectsGovernor } from './interaction-effects-governor.js';
|
|
96
99
|
import { VisibilityEpochTracker } from './visibility-epoch.js';
|
|
97
100
|
import { isEntityVisible } from './entity-visibility.js';
|
|
@@ -103,21 +106,21 @@ import { resolveEnvironment } from './environment.js';
|
|
|
103
106
|
import { ShadowPass, resolveShadowMapResolution } from './shadow-pass.js';
|
|
104
107
|
import { fitSunLightMatrix, cameraFrustumFocusCorners, resolveShadowNormalBiasMetres } from './shadow-light-matrix.js';
|
|
105
108
|
import { collectShadowOccluders } from './shadow-occluders.js';
|
|
106
|
-
import { uploadInstancedRteDeltas } from './instanced-rte.js';
|
|
109
|
+
import { drawInstanceRuns, uploadInstancedRteDeltas } from './instanced-rte.js';
|
|
110
|
+
import { INSTANCED_RTE_DELTA_SLOT } from './instanced-vertex-layout.js';
|
|
107
111
|
import { shadowOccluderBatches } from './shadow-occluder-batches.js';
|
|
108
112
|
import { captureRendererScreenshot } from './renderer-screenshot.js';
|
|
109
113
|
import { beginRendererColorFrameCapture, cancelRendererColorFrame, discardRendererColorFrameReadback, encodeRendererColorFrameCapture, requestRendererColorFrame, retryRendererColorFrame, settleRendererColorFrameCapture } from './renderer-color-readback.js';
|
|
110
114
|
import { shouldRouteMeshTransparent, shouldRouteBatchTransparent, splitVisibleIdsByPromotion, DEFAULT_GHOST_ALPHA } from './overlay-routing.js';
|
|
115
|
+
import { batchEntityIdAnchor, batchHasColorOverride, packOverrideParams } from './entity-color-table.js';
|
|
111
116
|
import { XRayAlpha, XRayEpochTracker } from './xray-alpha.js';
|
|
112
117
|
import { PartialBatchRequests } from './partial-batch-requests.js';
|
|
113
|
-
import {
|
|
118
|
+
import { uploadIndividualMesh } from './individual-mesh-upload.js';
|
|
114
119
|
import { PointCloudRenderer } from './pointcloud/point-cloud-renderer.js';
|
|
115
120
|
import { appendPointCloudChunk as appendPointCloudChunkImpl, beginPointCloudStream as beginPointCloudStreamImpl, endPointCloudStream as endPointCloudStreamImpl, removePointCloudAsset as removePointCloudAssetImpl, } from './pointcloud/point-cloud-stream-lifecycle.js';
|
|
116
121
|
import { DeviationComputer } from './deviation/deviation-computer.js';
|
|
117
122
|
import { runGuardedGpuUpload, isDeviceLossThrow } from './gpu-upload-guard.js';
|
|
118
123
|
import { recoverRendererDevice, rendererDeviceLostError } from './device-recovery.js';
|
|
119
|
-
const MAX_ENCODED_ENTITY_ID = 0xFFFFFF;
|
|
120
|
-
let warnedEntityIdRange = false;
|
|
121
124
|
/**
|
|
122
125
|
* The reason `whenReady()` rejects when the renderer is destroyed.
|
|
123
126
|
*
|
|
@@ -165,6 +168,8 @@ export class Renderer {
|
|
|
165
168
|
// Ambient occlusion, separation lines and eye-dome lighting (post-pass-chain.ts),
|
|
166
169
|
// created on the first rendered frame.
|
|
167
170
|
postPasses = null;
|
|
171
|
+
/** GPU copies of the hovered entity for the hover outline (#5390); see hover-mesh-cache.ts. */
|
|
172
|
+
hoverMeshes = new HoverMeshCache();
|
|
168
173
|
/** Device px per CSS px in the drawing buffer; CSS-px sizes scale by it (#5383). */
|
|
169
174
|
pixelRatio = 1;
|
|
170
175
|
interactionEffects = new InteractionEffectsGovernor();
|
|
@@ -372,6 +377,8 @@ export class Renderer {
|
|
|
372
377
|
_xrayEpochs = new XRayEpochTracker();
|
|
373
378
|
_visibilityVersion = 0;
|
|
374
379
|
_partialBatchEpoch = 0;
|
|
380
|
+
_xrayAlpha = null;
|
|
381
|
+
_xrayAlphaEpoch = -1;
|
|
375
382
|
_lastColorOverrideGen = -1;
|
|
376
383
|
_xrayVersion = 0;
|
|
377
384
|
_lastHadPartialSources = false;
|
|
@@ -400,6 +407,8 @@ export class Renderer {
|
|
|
400
407
|
// One 336-byte buffer serves all frame batches/meshes (84 floats including RTE lanes).
|
|
401
408
|
uniformScratch = new Float32Array(MESH_UNIFORM_FLOATS);
|
|
402
409
|
uniformScratchU32 = new Uint32Array(this.uniformScratch.buffer, MESH_FLAGS_BYTE_OFFSET, 4);
|
|
410
|
+
// overrideParams lanes (#6076): zero except inside a painting renderBatch call.
|
|
411
|
+
uniformOverrideU32 = new Uint32Array(this.uniformScratch.buffer, MESH_UNIFORM_OFFSET.overrideParams * 4, 4);
|
|
403
412
|
// What the last render() actually clipped, so the GPU picker can mirror it and
|
|
404
413
|
// section/crop-clipped geometry stays unpickable, not just invisible. Updated
|
|
405
414
|
// every render; read by pick()/pickRect(). null = nothing clipped that frame.
|
|
@@ -979,6 +988,15 @@ export class Renderer {
|
|
|
979
988
|
requestRender: () => this.requestRender(),
|
|
980
989
|
});
|
|
981
990
|
}
|
|
991
|
+
/** Read per-scan-asset signed-distance statistics after a completed run. */
|
|
992
|
+
async readDeviationAssetStats() {
|
|
993
|
+
return this.deviationComputer.readAssetStats({
|
|
994
|
+
device: this.device,
|
|
995
|
+
scene: this.scene,
|
|
996
|
+
pointCloudRenderer: this.pointCloudRenderer,
|
|
997
|
+
requestRender: () => this.requestRender(),
|
|
998
|
+
});
|
|
999
|
+
}
|
|
982
1000
|
/**
|
|
983
1001
|
* Toggle Eye-Dome Lighting and tune its strength.
|
|
984
1002
|
*
|
|
@@ -1152,6 +1170,10 @@ export class Renderer {
|
|
|
1152
1170
|
createMeshFromData(meshData) {
|
|
1153
1171
|
return runGuardedGpuUpload(() => this.isDeviceLost(), () => this.createMeshFromDataUnguarded(meshData), (error) => this.reportUploadLoss(error));
|
|
1154
1172
|
}
|
|
1173
|
+
/** The source batch's frame for an individual copy of `meshData` (see individual-mesh-upload.ts). */
|
|
1174
|
+
individualMeshFrame(meshData) {
|
|
1175
|
+
return { sharedOrigin: this.scene.getSharedFrameOrigin(meshData.modelIndex, meshData), quantized: this.scene.isMeshQuantized(meshData) };
|
|
1176
|
+
}
|
|
1155
1177
|
/**
|
|
1156
1178
|
* Create a GPU Mesh from MeshData (lazy creation for selection highlighting)
|
|
1157
1179
|
* This is called on-demand when a mesh is selected, avoiding 2x buffer creation during streaming
|
|
@@ -1159,71 +1181,7 @@ export class Renderer {
|
|
|
1159
1181
|
createMeshFromDataUnguarded(meshData) {
|
|
1160
1182
|
if (!this.device.isInitialized())
|
|
1161
1183
|
return;
|
|
1162
|
-
const
|
|
1163
|
-
const vertexCount = meshData.positions.length / 3;
|
|
1164
|
-
const interleavedRaw = new ArrayBuffer(vertexCount * 7 * 4);
|
|
1165
|
-
const interleaved = new Float32Array(interleavedRaw);
|
|
1166
|
-
const interleavedU32 = new Uint32Array(interleavedRaw);
|
|
1167
|
-
// Build this individual mesh (selection highlight + GPU object-id picker)
|
|
1168
|
-
// in the same small local frame as its source batch.
|
|
1169
|
-
// CRITICAL: replicate the BATCH's exact two-step f32 path so the highlight
|
|
1170
|
-
// is bit-coincident with its source surface (no z-fight, no depth bias):
|
|
1171
|
-
// batch stores s = f32(local + (origin - sharedOrigin)) [merge]
|
|
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.
|
|
1175
|
-
const o = meshData.origin;
|
|
1176
|
-
const so = this.scene.getSharedFrameOrigin(meshData.modelIndex, meshData);
|
|
1177
|
-
const ox = o ? o[0] : 0, oy = o ? o[1] : 0, oz = o ? o[2] : 0;
|
|
1178
|
-
const fr = Math.fround;
|
|
1179
|
-
const dx = so ? (ox - so[0]) : ox, dy = so ? (oy - so[1]) : oy, dz = so ? (oz - so[2]) : oz;
|
|
1180
|
-
// Quantized batches (issue #1682 phase 6) render lattice-snapped
|
|
1181
|
-
// positions: the shader's quantMin + q*step is exactly the lattice
|
|
1182
|
-
// node nearest the batch's stored f32 rel coordinate. Reproduce it by
|
|
1183
|
-
// snapping the SAME rel coordinate here (round(s*1024)/1024 in f64
|
|
1184
|
-
// yields the identical exact-f32 lattice value — see quantize.ts), so
|
|
1185
|
-
// the highlight/picker mesh stays BIT-coincident with its quantized
|
|
1186
|
-
// source surface, exactly as the two-step fold above achieves for the
|
|
1187
|
-
// f32 path. Meshes whose batch fell back to f32 must not snap.
|
|
1188
|
-
const snap = this.scene.isMeshQuantized(meshData)
|
|
1189
|
-
? (v) => Math.round(v * 1024) / 1024
|
|
1190
|
-
: (v) => v;
|
|
1191
|
-
const p = meshData.positions;
|
|
1192
|
-
for (let i = 0; i < vertexCount; i++) {
|
|
1193
|
-
const base = i * 7;
|
|
1194
|
-
const posBase = i * 3;
|
|
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]);
|
|
1198
|
-
const hasNormals = meshData.normals.length > 0;
|
|
1199
|
-
interleaved[base + 3] = hasNormals ? meshData.normals[posBase] : 0;
|
|
1200
|
-
interleaved[base + 4] = hasNormals ? meshData.normals[posBase + 1] : 0;
|
|
1201
|
-
interleaved[base + 5] = hasNormals ? meshData.normals[posBase + 2] : 0;
|
|
1202
|
-
let encodedId = meshData.expressId >>> 0;
|
|
1203
|
-
if (encodedId > MAX_ENCODED_ENTITY_ID) {
|
|
1204
|
-
if (!warnedEntityIdRange) {
|
|
1205
|
-
warnedEntityIdRange = true;
|
|
1206
|
-
console.warn('[Renderer] expressId exceeds 24-bit seam-ID encoding range; seam lines may collide.');
|
|
1207
|
-
}
|
|
1208
|
-
encodedId = encodedId & MAX_ENCODED_ENTITY_ID;
|
|
1209
|
-
}
|
|
1210
|
-
// Stamp the SAME high-byte material-colour salt as the batch path
|
|
1211
|
-
// (mergeGeometry) so this individual/selection mesh computes the
|
|
1212
|
-
// identical depth nudge as its source batch — otherwise the highlight
|
|
1213
|
-
// (selection pipeline, reverse-Z 'greater-equal') would z-fight or drop
|
|
1214
|
-
// out against the salted base depth. Low 24 bits stay the picking id.
|
|
1215
|
-
interleavedU32[base + 6] = packEntityLane(encodedId, colorSaltByte(meshData.color));
|
|
1216
|
-
}
|
|
1217
|
-
const vertexBuffer = device.createBuffer({
|
|
1218
|
-
size: interleaved.byteLength,
|
|
1219
|
-
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
|
|
1220
|
-
});
|
|
1221
|
-
device.queue.writeBuffer(vertexBuffer, 0, interleaved);
|
|
1222
|
-
const indexBuffer = device.createBuffer({
|
|
1223
|
-
size: meshData.indices.byteLength,
|
|
1224
|
-
usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
|
|
1225
|
-
});
|
|
1226
|
-
device.queue.writeBuffer(indexBuffer, 0, meshData.indices);
|
|
1184
|
+
const { vertexBuffer, indexBuffer, indexCount, transform, rteOrigin } = uploadIndividualMesh(this.device.getDevice(), meshData, this.individualMeshFrame(meshData));
|
|
1227
1185
|
// Keep the hydrated mesh in its decoded local frame. Folding this
|
|
1228
1186
|
// origin back into f32 vertices used to make selection/highlight the
|
|
1229
1187
|
// only mesh path that lost centimetres at national-grid coordinates.
|
|
@@ -1240,16 +1198,11 @@ export class Renderer {
|
|
|
1240
1198
|
geometryItemId: reportableItemId(meshData, meshData.expressId),
|
|
1241
1199
|
vertexBuffer,
|
|
1242
1200
|
indexBuffer,
|
|
1243
|
-
indexCount
|
|
1244
|
-
transform
|
|
1245
|
-
|
|
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],
|
|
1201
|
+
indexCount,
|
|
1202
|
+
transform,
|
|
1203
|
+
rteOrigin,
|
|
1252
1204
|
color: meshData.color,
|
|
1205
|
+
...(meshData.material ? { finish: meshData.material } : {}), // #5582
|
|
1253
1206
|
hydrated: true,
|
|
1254
1207
|
});
|
|
1255
1208
|
}
|
|
@@ -1519,14 +1472,6 @@ export class Renderer {
|
|
|
1519
1472
|
// governor's verdict so early navigation can't degrade a session.
|
|
1520
1473
|
const timingUnstable = options.isStreaming === true || this.scene.hasQueuedMeshes();
|
|
1521
1474
|
const effectsLive = this.interactionEffects.frame(interacting, performance.now(), timingUnstable, options.interactionFrameIntervalMs ?? 0);
|
|
1522
|
-
// Edge contrast is NOT interaction-gated: its per-fragment work runs
|
|
1523
|
-
// unconditionally in the shader and the gated tail is a handful of
|
|
1524
|
-
// ALU ops, so disabling it bought nothing and only made the crease
|
|
1525
|
-
// darkening pop off/on around gestures (visible in ortho).
|
|
1526
|
-
const edgeEnabled = visualEnhancement.enabled && visualEnhancement.edgeContrast.enabled;
|
|
1527
|
-
const edgeIntensity = Math.min(3.0, Math.max(0.0, visualEnhancement.edgeContrast.intensity));
|
|
1528
|
-
const edgeEnabledU32 = edgeEnabled ? 1 : 0;
|
|
1529
|
-
const edgeIntensityMilliU32 = Math.round(edgeIntensity * 1000);
|
|
1530
1475
|
// Only the edge pass reads the object-id attachment after the pass.
|
|
1531
1476
|
const needsObjectIdPass = livePostEffects(visualEnhancement, effectsLive).edges;
|
|
1532
1477
|
// Check if visibility filtering is active
|
|
@@ -1603,10 +1548,6 @@ export class Renderer {
|
|
|
1603
1548
|
// it carries a per-instance hidden flag the shader discards on). Diffed → a
|
|
1604
1549
|
// no-op when visibility is unchanged.
|
|
1605
1550
|
this.scene.setInstancedVisibility(options.hiddenIds, options.isolatedIds);
|
|
1606
|
-
// Per-frame alpha overrides for X-Ray mode. See RenderOptions.transparencyOverrides.
|
|
1607
|
-
// XRayAlpha snapshots the caller's map so mid-frame mutation can't desync
|
|
1608
|
-
// classification and uniform-write decisions for the same batch/mesh, and
|
|
1609
|
-
// owns the per-entity resolution + the mixed-batch partition (#4129).
|
|
1610
1551
|
// X-Ray *context* mode (`ghostExceptIds`) feeds the same machinery, so it
|
|
1611
1552
|
// routes through the transparent pipeline with no extra call sites — and
|
|
1612
1553
|
// avoids building a Map over every element just to fade "the rest".
|
|
@@ -1616,16 +1557,19 @@ export class Renderer {
|
|
|
1616
1557
|
// the user asked to fade the building and got a solid facade standing
|
|
1617
1558
|
// in front of a ghosted interior.
|
|
1618
1559
|
this.scene.setInstancedGhosting(options.ghostExceptIds ?? null, selectedExpressIds, ghostAlpha);
|
|
1619
|
-
|
|
1560
|
+
if (this._xrayAlpha === null || this._xrayAlphaEpoch !== this._partialBatchEpoch) {
|
|
1561
|
+
this._xrayAlpha = new XRayAlpha(options, selectedExpressIds);
|
|
1562
|
+
this._xrayAlphaEpoch = this._partialBatchEpoch;
|
|
1563
|
+
}
|
|
1564
|
+
const xray = this._xrayAlpha;
|
|
1620
1565
|
const alphaForMesh = (expressId, fallback) => xray.forEntity(expressId, fallback);
|
|
1621
1566
|
const alphaForBatch = (batch, fallback) => xray.forBatch(batch, fallback);
|
|
1622
1567
|
// Lens / Pset color overrides: when an entity has an override, force
|
|
1623
|
-
// its base draw through the opaque pipeline
|
|
1624
|
-
//
|
|
1625
|
-
//
|
|
1626
|
-
//
|
|
1627
|
-
//
|
|
1628
|
-
// there.
|
|
1568
|
+
// its base draw through the opaque pipeline — the only draws the
|
|
1569
|
+
// entity colour table paints (#6076) — or an entity whose default
|
|
1570
|
+
// pipeline is transparent (IfcSpace, IfcOpeningElement, glass, …)
|
|
1571
|
+
// would never show it. See issue #677. Pure routing decision lives in
|
|
1572
|
+
// overlay-routing.ts and is unit-tested there.
|
|
1629
1573
|
const colorOverrides = this.scene.getColorOverrides();
|
|
1630
1574
|
// PERFORMANCE FIX: Use batch-level visibility filtering instead of creating individual meshes
|
|
1631
1575
|
// Only create individual meshes for selected elements (for highlighting)
|
|
@@ -1753,6 +1697,11 @@ export class Renderer {
|
|
|
1753
1697
|
max: [...options.clipBox.max],
|
|
1754
1698
|
}
|
|
1755
1699
|
: null;
|
|
1700
|
+
// Meshes this camera's RTE frame cannot represent (#6128). Nothing
|
|
1701
|
+
// culls individual meshes before this point, so a far-off mesh (a
|
|
1702
|
+
// stray element, one hydrated for picking) must be skipped, not
|
|
1703
|
+
// allowed to fail every frame.
|
|
1704
|
+
const outsideEyeEnvelope = new Set();
|
|
1756
1705
|
// Reuse pooled scratch buffer for per-mesh uniform writes
|
|
1757
1706
|
const meshBuf = this.uniformScratch;
|
|
1758
1707
|
const meshFlags = this.uniformScratchU32;
|
|
@@ -1773,7 +1722,7 @@ export class Renderer {
|
|
|
1773
1722
|
meshBuf[34] = mesh.color[2];
|
|
1774
1723
|
// Selected meshes always keep their own alpha so highlights stay opaque
|
|
1775
1724
|
meshBuf[35] = isSelected ? mesh.color[3] : alphaForMesh(mesh.expressId, mesh.color[3]);
|
|
1776
|
-
packMeshMaterial(meshBuf, mesh.color[3], mesh.material);
|
|
1725
|
+
packMeshMaterial(meshBuf, mesh.color[3], mesh.finish ?? mesh.material);
|
|
1777
1726
|
// Section plane data (offset 40-43)
|
|
1778
1727
|
if (sectionPlaneData) {
|
|
1779
1728
|
meshBuf[40] = sectionPlaneData.normal[0];
|
|
@@ -1796,14 +1745,17 @@ export class Renderer {
|
|
|
1796
1745
|
(sectionPlaneData?.enabled ? 1 : 0) |
|
|
1797
1746
|
(options.sectionPlane?.flipped ? 2 : 0) |
|
|
1798
1747
|
clipBit;
|
|
1799
|
-
meshFlags[2] =
|
|
1800
|
-
meshFlags[3] =
|
|
1748
|
+
meshFlags[2] = 0; // unused since #5746 (was the derivative edge darkening)
|
|
1749
|
+
meshFlags[3] = 0;
|
|
1801
1750
|
// Individual meshes retain their origin and share the RTE
|
|
1802
1751
|
// fragment contract with colour batches.
|
|
1803
1752
|
packRteFragmentSpace(relativeToEyeFrame, sectionPlaneData, options.clipBox, meshBuf);
|
|
1804
1753
|
const meshOrigin = mesh.rteOrigin
|
|
1805
1754
|
?? [mesh.transform.m[12], mesh.transform.m[13], mesh.transform.m[14]];
|
|
1806
|
-
relativeToEyeFrame.
|
|
1755
|
+
if (!relativeToEyeFrame.tryPackDrawableOrigin(meshOrigin, meshBuf, MESH_UNIFORM_OFFSET.drawableDelta)) {
|
|
1756
|
+
outsideEyeEnvelope.add(mesh);
|
|
1757
|
+
continue;
|
|
1758
|
+
}
|
|
1807
1759
|
meshFlags[0] |= MESH_FLAG_RTE_DRAWABLE;
|
|
1808
1760
|
device.queue.writeBuffer(mesh.uniformBuffer, 0, meshBuf);
|
|
1809
1761
|
}
|
|
@@ -2041,7 +1993,13 @@ export class Renderer {
|
|
|
2041
1993
|
// only presets that enable the sky — blanked the model on those
|
|
2042
1994
|
// drivers. Binding while the main pipeline is current keeps it
|
|
2043
1995
|
// valid for every main-family draw that follows.
|
|
1996
|
+
this.pipeline.setEntityColorTableBuffer(this.scene.getEntityColorTable().getBuffer());
|
|
2044
1997
|
pass.setBindGroup(1, this.pipeline.getEnvironmentBindGroup());
|
|
1998
|
+
// Selected meshes drawn this frame, for the selection-mask pass
|
|
1999
|
+
// (#5390) after `pass.end()` below. Populated inside the batched
|
|
2000
|
+
// branch, where the highlight draw itself happens; empty in the
|
|
2001
|
+
// no-batches fallback, which does not draw selection either.
|
|
2002
|
+
let selectedMeshesForMask = [];
|
|
2045
2003
|
// Check if we have batched meshes (preferred for performance)
|
|
2046
2004
|
const allBatchedMeshes = this.scene.getBatchedMeshes();
|
|
2047
2005
|
// PERFORMANCE FIX: Always use batch rendering when we have batches
|
|
@@ -2271,22 +2229,25 @@ export class Renderer {
|
|
|
2271
2229
|
// x = isSelected (0/1)
|
|
2272
2230
|
// y = section/clip bitfield:
|
|
2273
2231
|
// bit 0 = sectionEnabled, bit 1 = flipped, bit 2 = clipBoxEnabled
|
|
2274
|
-
// z =
|
|
2275
|
-
//
|
|
2232
|
+
// z, w = unused, written as 0. They fed the in-shader derivative
|
|
2233
|
+
// edge darkening, removed in #5746; edges come from the
|
|
2234
|
+
// edge pass. The lanes stay so the struct layout is unchanged.
|
|
2276
2235
|
tplFlags[0] = 0;
|
|
2277
2236
|
tplFlags[1] =
|
|
2278
2237
|
(sectionPlaneData?.enabled ? 1 : 0) |
|
|
2279
2238
|
(options.sectionPlane?.flipped ? 2 : 0) |
|
|
2280
2239
|
tplClipBit;
|
|
2281
|
-
tplFlags[2] =
|
|
2282
|
-
tplFlags[3] =
|
|
2240
|
+
tplFlags[2] = 0;
|
|
2241
|
+
tplFlags[3] = 0;
|
|
2283
2242
|
// Flat/quantized/textured batches enter WGSL in the single
|
|
2284
2243
|
// camera-relative frame. Their fragment clip inputs must use
|
|
2285
2244
|
// that same frame; mixing a local vertex with a 5,000 km f32
|
|
2286
2245
|
// world plane loses centimetre cuts before the comparison.
|
|
2287
2246
|
packRteFragmentSpace(relativeToEyeFrame, sectionPlaneData, options.clipBox, tpl);
|
|
2288
|
-
// Helper function to render a batch — patches color into the shared template
|
|
2289
|
-
|
|
2247
|
+
// Helper function to render a batch — patches color into the shared template.
|
|
2248
|
+
// `paint` = a depth-writing opaque draw: colour overrides shade it from the
|
|
2249
|
+
// entity colour table (#6076); transparent draws keep their own colour.
|
|
2250
|
+
const renderBatch = (batch, paint = false) => {
|
|
2290
2251
|
if (!batch.bindGroup || !batch.uniformBuffer)
|
|
2291
2252
|
return;
|
|
2292
2253
|
// Patch the per-batch colour, and the material its AUTHORED
|
|
@@ -2295,7 +2256,7 @@ export class Renderer {
|
|
|
2295
2256
|
tpl[33] = batch.color[1];
|
|
2296
2257
|
tpl[34] = batch.color[2];
|
|
2297
2258
|
tpl[35] = alphaForBatch(batch, batch.color[3]);
|
|
2298
|
-
packMeshMaterial(tpl, batch.color[3]);
|
|
2259
|
+
packMeshMaterial(tpl, batch.color[3], batch.finish);
|
|
2299
2260
|
// Per-batch local frame: the batch's vertices are stored
|
|
2300
2261
|
// RELATIVE to batch.origin (f32-small), so set the model
|
|
2301
2262
|
// matrix translation column to origin (world = origin + pos).
|
|
@@ -2309,7 +2270,8 @@ export class Renderer {
|
|
|
2309
2270
|
// The regular model matrix remains for absolute-space
|
|
2310
2271
|
// fragment work (section/shadow); vertex projection uses
|
|
2311
2272
|
// this f64-subtracted high/low origin instead.
|
|
2312
|
-
relativeToEyeFrame.
|
|
2273
|
+
if (!relativeToEyeFrame.tryPackDrawableOrigin(o ?? [0, 0, 0], tpl, MESH_UNIFORM_OFFSET.drawableDelta))
|
|
2274
|
+
return;
|
|
2313
2275
|
tplFlags[0] |= MESH_FLAG_RTE_DRAWABLE;
|
|
2314
2276
|
// Quantized dequantization params (issue #1682 phase 6);
|
|
2315
2277
|
// zeroed for f32 batches (their pipelines ignore them).
|
|
@@ -2318,7 +2280,10 @@ export class Renderer {
|
|
|
2318
2280
|
tpl[57] = qz ? qz.min[1] : 0;
|
|
2319
2281
|
tpl[58] = qz ? qz.min[2] : 0;
|
|
2320
2282
|
tpl[59] = qz ? qz.step : 0;
|
|
2283
|
+
const painted = paint && batchHasColorOverride(batch, colorOverrides, colorOverrideGen);
|
|
2284
|
+
packOverrideParams(this.uniformOverrideU32, painted ? batchEntityIdAnchor(batch) : null, painted, options.emphasizeOverrides === true);
|
|
2321
2285
|
device.queue.writeBuffer(batch.uniformBuffer, 0, tpl);
|
|
2286
|
+
this.uniformOverrideU32.fill(0);
|
|
2322
2287
|
// Single draw call for entire batch! LOD1-selected batches
|
|
2323
2288
|
// bind their simplified index range over the same vertices.
|
|
2324
2289
|
const useLod1 = batch.lod1IndexBuffer && batch.lod1IndexCount && lodBatches.has(batch.id);
|
|
@@ -2341,11 +2306,7 @@ export class Renderer {
|
|
|
2341
2306
|
// probe (enableQuantizedBatches) verified they exist, so the
|
|
2342
2307
|
// base fallback here is type-safety, never taken.
|
|
2343
2308
|
const pipeFor = (batch, kind) => {
|
|
2344
|
-
const base = kind === 'opaque'
|
|
2345
|
-
? this.pipeline.getPipeline()
|
|
2346
|
-
: kind === 'transparent'
|
|
2347
|
-
? this.pipeline.getTransparentPipeline()
|
|
2348
|
-
: this.pipeline.getOverlayPipeline();
|
|
2309
|
+
const base = kind === 'opaque' ? this.pipeline.getPipeline() : this.pipeline.getTransparentPipeline();
|
|
2349
2310
|
if (!batch.quantized)
|
|
2350
2311
|
return base;
|
|
2351
2312
|
return this.pipeline.getQuantizedPipelineVariant(kind) ?? base;
|
|
@@ -2362,7 +2323,7 @@ export class Renderer {
|
|
|
2362
2323
|
pass.setPipeline(this.pipeline.getPipeline());
|
|
2363
2324
|
for (const batch of opaqueBatches) {
|
|
2364
2325
|
pass.setPipeline(pipeFor(batch, 'opaque'));
|
|
2365
|
-
renderBatch(batch);
|
|
2326
|
+
renderBatch(batch, true);
|
|
2366
2327
|
}
|
|
2367
2328
|
pass.setPipeline(this.pipeline.getPipeline());
|
|
2368
2329
|
// GPU-instancing pass — repeated geometry collated by the producer
|
|
@@ -2407,8 +2368,11 @@ export class Renderer {
|
|
|
2407
2368
|
}
|
|
2408
2369
|
visibleInstanced = kept;
|
|
2409
2370
|
}
|
|
2371
|
+
// Per-template instance runs inside this frame's RTE envelope,
|
|
2372
|
+
// shared by the opaque and transparent instanced sub-passes.
|
|
2373
|
+
let visibleInstancedRuns = [];
|
|
2410
2374
|
if (visibleInstanced.length > 0) {
|
|
2411
|
-
uploadInstancedRteDeltas(device, visibleInstanced, relativeToEyeFrame.getCameraWorld());
|
|
2375
|
+
visibleInstancedRuns = uploadInstancedRteDeltas(device, visibleInstanced, relativeToEyeFrame.getCameraWorld());
|
|
2412
2376
|
// Opaque instanced pass. flags.x bit 2 marks "instanced pass" so the
|
|
2413
2377
|
// shader routes per-instance opacity: opaque (or selected) occurrences
|
|
2414
2378
|
// draw here; translucent ones (lens/x-ray/compare overrides) are
|
|
@@ -2417,13 +2381,15 @@ export class Renderer {
|
|
|
2417
2381
|
pass.setPipeline(this.pipeline.getInstancedPipeline());
|
|
2418
2382
|
pass.setBindGroup(0, this.pipeline.getBindGroup());
|
|
2419
2383
|
pass.setBindGroup(1, this.pipeline.getEnvironmentBindGroup());
|
|
2420
|
-
for (const it of visibleInstanced) {
|
|
2384
|
+
for (const [i, it] of visibleInstanced.entries()) {
|
|
2421
2385
|
pass.setVertexBuffer(0, it.vertexBuffer);
|
|
2422
2386
|
pass.setVertexBuffer(1, it.instanceBuffer);
|
|
2387
|
+
pass.setVertexBuffer(INSTANCED_RTE_DELTA_SLOT, it.rteDeltas.buffer);
|
|
2423
2388
|
pass.setIndexBuffer(it.indexBuffer, 'uint32');
|
|
2424
|
-
|
|
2425
|
-
frameDrawCalls
|
|
2426
|
-
|
|
2389
|
+
const runs = visibleInstancedRuns[i];
|
|
2390
|
+
frameDrawCalls += drawInstanceRuns(pass, it.indexCount, runs);
|
|
2391
|
+
if (runs.length > 0)
|
|
2392
|
+
frameInstancedDrawn++;
|
|
2427
2393
|
}
|
|
2428
2394
|
pass.setPipeline(this.pipeline.getPipeline());
|
|
2429
2395
|
// The TRANSPARENT instanced sub-pass is drawn later, alongside the
|
|
@@ -2458,7 +2424,7 @@ export class Renderer {
|
|
|
2458
2424
|
if (txAlpha <= 0.01)
|
|
2459
2425
|
continue;
|
|
2460
2426
|
// Lens / Pset colour override tints the sampled texel — the
|
|
2461
|
-
//
|
|
2427
|
+
// entity colour table is off for textured draws (#6076),
|
|
2462
2428
|
// so applying the override here is what recolours them.
|
|
2463
2429
|
const txOverride = colorOverrides?.get(tm.expressId);
|
|
2464
2430
|
// `world = origin + position`: the vertex buffer stores
|
|
@@ -2473,7 +2439,8 @@ export class Renderer {
|
|
|
2473
2439
|
tpl[28] = tm.origin[0];
|
|
2474
2440
|
tpl[29] = tm.origin[1];
|
|
2475
2441
|
tpl[30] = tm.origin[2];
|
|
2476
|
-
relativeToEyeFrame.
|
|
2442
|
+
if (!relativeToEyeFrame.tryPackDrawableOrigin(tm.origin, tpl, MESH_UNIFORM_OFFSET.drawableDelta))
|
|
2443
|
+
continue;
|
|
2477
2444
|
tplFlags[0] |= MESH_FLAG_RTE_DRAWABLE;
|
|
2478
2445
|
tpl[32] = txOverride ? txOverride[0] : tm.color[0];
|
|
2479
2446
|
tpl[33] = txOverride ? txOverride[1] : tm.color[1];
|
|
@@ -2488,7 +2455,7 @@ export class Renderer {
|
|
|
2488
2455
|
// never actually blends here — only the glass/roughness
|
|
2489
2456
|
// CHOICE in the shader follows the same rule as everywhere
|
|
2490
2457
|
// else, for the same reason `tm.material` does.
|
|
2491
|
-
packMeshMaterial(tpl, tm.color[3], tm.material);
|
|
2458
|
+
packMeshMaterial(tpl, tm.color[3], tm.finish ?? tm.material);
|
|
2492
2459
|
device.queue.writeBuffer(tm.uniformBuffer, 0, tpl);
|
|
2493
2460
|
pass.setBindGroup(0, tm.bindGroup);
|
|
2494
2461
|
pass.setVertexBuffer(0, tm.vertexBuffer);
|
|
@@ -2519,7 +2486,7 @@ export class Renderer {
|
|
|
2519
2486
|
// Use opaque or transparent pipeline based on resolved alpha
|
|
2520
2487
|
// (not the parent batch's color[3] — that ignores transparencyOverrides).
|
|
2521
2488
|
// Promote to opaque if any expressId in the sub-batch carries a
|
|
2522
|
-
// lens/Pset colour override, so the
|
|
2489
|
+
// lens/Pset colour override, so the colour table paints it (#6076).
|
|
2523
2490
|
const isTransparent = shouldRouteBatchTransparent(alphaForBatch(subBatch, color[3]), subBatch.expressIds, colorOverrides);
|
|
2524
2491
|
if (isTransparent) {
|
|
2525
2492
|
transparentSubBatches.push(subBatch);
|
|
@@ -2533,7 +2500,7 @@ export class Renderer {
|
|
|
2533
2500
|
pass.setPipeline(pipeFor(subBatch, 'opaque'));
|
|
2534
2501
|
opaqueSubBatches.push(subBatch);
|
|
2535
2502
|
// Render the sub-batch as a single draw call
|
|
2536
|
-
renderBatch(subBatch);
|
|
2503
|
+
renderBatch(subBatch, true);
|
|
2537
2504
|
}
|
|
2538
2505
|
}
|
|
2539
2506
|
for (const subBatch of transparentSubBatches) {
|
|
@@ -2543,28 +2510,6 @@ export class Renderer {
|
|
|
2543
2510
|
// Reset to opaque pipeline for subsequent rendering
|
|
2544
2511
|
pass.setPipeline(this.pipeline.getPipeline());
|
|
2545
2512
|
}
|
|
2546
|
-
// Render color overlay batches (lens coloring) on top of ALL opaque geometry.
|
|
2547
|
-
// Placed AFTER partial batches so depth buffer is complete for both full
|
|
2548
|
-
// and partial batches. Uses 'equal' depth compare — only paints where
|
|
2549
|
-
// original geometry wrote depth, so hidden entities never leak through.
|
|
2550
|
-
//
|
|
2551
|
-
// flags.x bit 1 = overlay: tells the shader to preserve baseColor.a
|
|
2552
|
-
// (the overlay pipeline now has src-alpha blending so low-alpha ghost
|
|
2553
|
-
// tints composite correctly against the opaque pass) AND skip the
|
|
2554
|
-
// specular term (glass reflections are meant for real glass and
|
|
2555
|
-
// would whiten low-alpha colour overrides at grazing angles).
|
|
2556
|
-
const overrideBatches = this.scene.getOverrideBatches();
|
|
2557
|
-
if (overrideBatches.length > 0) {
|
|
2558
|
-
pass.setPipeline(this.pipeline.getOverlayPipeline());
|
|
2559
|
-
// bit 1 = overlay; bit 5 (32) = emphasize (pop) — see shader.
|
|
2560
|
-
tplFlags[0] = options.emphasizeOverrides ? (2 | 32) : 2;
|
|
2561
|
-
for (const batch of overrideBatches) {
|
|
2562
|
-
pass.setPipeline(pipeFor(batch, 'overlay'));
|
|
2563
|
-
renderBatch(batch);
|
|
2564
|
-
}
|
|
2565
|
-
tplFlags[0] = 0; // restore for any downstream use of the template
|
|
2566
|
-
pass.setPipeline(this.pipeline.getPipeline());
|
|
2567
|
-
}
|
|
2568
2513
|
// Filled, hatched 3D cut surfaces are now rendered by
|
|
2569
2514
|
// Section2DOverlayRenderer using the exact polygons from
|
|
2570
2515
|
// SectionCutter (triangle-plane intersection). The old
|
|
@@ -2622,6 +2567,7 @@ export class Renderer {
|
|
|
2622
2567
|
return true;
|
|
2623
2568
|
})
|
|
2624
2569
|
: [];
|
|
2570
|
+
selectedMeshesForMask = selectedMeshes;
|
|
2625
2571
|
// Transparent instanced sub-pass — drawn here (after ALL opaque incl. the
|
|
2626
2572
|
// textured sub-pass) so ghosted/x-rayed instanced occurrences blend over
|
|
2627
2573
|
// a complete depth buffer. Only runs when an override actually made some
|
|
@@ -2635,12 +2581,12 @@ export class Renderer {
|
|
|
2635
2581
|
pass.setPipeline(instancedTransparentPipeline);
|
|
2636
2582
|
pass.setBindGroup(0, this.pipeline.getBindGroup());
|
|
2637
2583
|
pass.setBindGroup(1, this.pipeline.getEnvironmentBindGroup());
|
|
2638
|
-
for (const it of visibleInstanced) {
|
|
2584
|
+
for (const [i, it] of visibleInstanced.entries()) {
|
|
2639
2585
|
pass.setVertexBuffer(0, it.vertexBuffer);
|
|
2640
2586
|
pass.setVertexBuffer(1, it.instanceBuffer);
|
|
2587
|
+
pass.setVertexBuffer(INSTANCED_RTE_DELTA_SLOT, it.rteDeltas.buffer);
|
|
2641
2588
|
pass.setIndexBuffer(it.indexBuffer, 'uint32');
|
|
2642
|
-
pass
|
|
2643
|
-
frameDrawCalls++;
|
|
2589
|
+
frameDrawCalls += drawInstanceRuns(pass, it.indexCount, visibleInstancedRuns[i]);
|
|
2644
2590
|
}
|
|
2645
2591
|
pass.setPipeline(this.pipeline.getPipeline());
|
|
2646
2592
|
}
|
|
@@ -2665,7 +2611,7 @@ export class Renderer {
|
|
|
2665
2611
|
tpl[33] = mesh.color[1];
|
|
2666
2612
|
tpl[34] = mesh.color[2];
|
|
2667
2613
|
tpl[35] = alphaForMesh(mesh.expressId, mesh.color[3]);
|
|
2668
|
-
packMeshMaterial(tpl, mesh.color[3], mesh.material);
|
|
2614
|
+
packMeshMaterial(tpl, mesh.color[3], mesh.finish ?? mesh.material);
|
|
2669
2615
|
if (sectionPlaneData) {
|
|
2670
2616
|
tpl[40] = sectionPlaneData.normal[0];
|
|
2671
2617
|
tpl[41] = sectionPlaneData.normal[1];
|
|
@@ -2683,11 +2629,12 @@ export class Renderer {
|
|
|
2683
2629
|
(sectionPlaneData?.enabled ? 1 : 0) |
|
|
2684
2630
|
(options.sectionPlane?.flipped ? 2 : 0) |
|
|
2685
2631
|
tplClipBit;
|
|
2686
|
-
tplFlags[2] =
|
|
2687
|
-
tplFlags[3] =
|
|
2632
|
+
tplFlags[2] = 0;
|
|
2633
|
+
tplFlags[3] = 0;
|
|
2688
2634
|
packRteFragmentSpace(relativeToEyeFrame, sectionPlaneData, options.clipBox, tpl);
|
|
2689
2635
|
const transparentOrigin = mesh.rteOrigin ?? [mesh.transform.m[12], mesh.transform.m[13], mesh.transform.m[14]];
|
|
2690
|
-
relativeToEyeFrame.
|
|
2636
|
+
if (!relativeToEyeFrame.tryPackDrawableOrigin(transparentOrigin, tpl, MESH_UNIFORM_OFFSET.drawableDelta))
|
|
2637
|
+
continue;
|
|
2691
2638
|
tplFlags[0] |= MESH_FLAG_RTE_DRAWABLE;
|
|
2692
2639
|
device.queue.writeBuffer(mesh.uniformBuffer, 0, tpl);
|
|
2693
2640
|
pass.setBindGroup(0, mesh.bindGroup);
|
|
@@ -2726,7 +2673,7 @@ export class Renderer {
|
|
|
2726
2673
|
tpl[33] = mesh.color[1];
|
|
2727
2674
|
tpl[34] = mesh.color[2];
|
|
2728
2675
|
tpl[35] = mesh.color[3];
|
|
2729
|
-
packMeshMaterial(tpl, mesh.color[3], mesh.material);
|
|
2676
|
+
packMeshMaterial(tpl, mesh.color[3], mesh.finish ?? mesh.material);
|
|
2730
2677
|
if (sectionPlaneData) {
|
|
2731
2678
|
tpl[40] = sectionPlaneData.normal[0];
|
|
2732
2679
|
tpl[41] = sectionPlaneData.normal[1];
|
|
@@ -2744,11 +2691,12 @@ export class Renderer {
|
|
|
2744
2691
|
(sectionPlaneData?.enabled ? 1 : 0) |
|
|
2745
2692
|
(options.sectionPlane?.flipped ? 2 : 0) |
|
|
2746
2693
|
tplClipBit;
|
|
2747
|
-
tplFlags[2] =
|
|
2748
|
-
tplFlags[3] =
|
|
2694
|
+
tplFlags[2] = 0;
|
|
2695
|
+
tplFlags[3] = 0;
|
|
2749
2696
|
packRteFragmentSpace(relativeToEyeFrame, sectionPlaneData, options.clipBox, tpl);
|
|
2750
2697
|
const selectedOrigin = mesh.rteOrigin ?? [mesh.transform.m[12], mesh.transform.m[13], mesh.transform.m[14]];
|
|
2751
|
-
relativeToEyeFrame.
|
|
2698
|
+
if (!relativeToEyeFrame.tryPackDrawableOrigin(selectedOrigin, tpl, MESH_UNIFORM_OFFSET.drawableDelta))
|
|
2699
|
+
continue;
|
|
2752
2700
|
tplFlags[0] |= MESH_FLAG_RTE_DRAWABLE;
|
|
2753
2701
|
device.queue.writeBuffer(mesh.uniformBuffer, 0, tpl);
|
|
2754
2702
|
pass.setPipeline(this.pipeline.getSelectionPipeline());
|
|
@@ -2763,6 +2711,8 @@ export class Renderer {
|
|
|
2763
2711
|
// Fallback: render individual meshes (only when no batches exist)
|
|
2764
2712
|
// Render opaque meshes with per-mesh bind groups
|
|
2765
2713
|
for (const mesh of opaqueMeshes) {
|
|
2714
|
+
if (outsideEyeEnvelope.has(mesh))
|
|
2715
|
+
continue;
|
|
2766
2716
|
if (mesh.bindGroup) {
|
|
2767
2717
|
pass.setBindGroup(0, mesh.bindGroup);
|
|
2768
2718
|
}
|
|
@@ -2778,6 +2728,8 @@ export class Renderer {
|
|
|
2778
2728
|
if (transparentMeshes.length > 0) {
|
|
2779
2729
|
pass.setPipeline(this.pipeline.getTransparentPipeline());
|
|
2780
2730
|
for (const mesh of transparentMeshes) {
|
|
2731
|
+
if (outsideEyeEnvelope.has(mesh))
|
|
2732
|
+
continue;
|
|
2781
2733
|
if (mesh.bindGroup) {
|
|
2782
2734
|
pass.setBindGroup(0, mesh.bindGroup);
|
|
2783
2735
|
}
|
|
@@ -2822,8 +2774,25 @@ export class Renderer {
|
|
|
2822
2774
|
rteCamera: relativeToEyeFrame.getCameraWorld(),
|
|
2823
2775
|
});
|
|
2824
2776
|
pass.end();
|
|
2777
|
+
// Selection/hover outline input (#5390): built from the meshes the
|
|
2778
|
+
// highlight-draw loop above already prepared this frame.
|
|
2779
|
+
// A hidden / isolated-out entity is never outlined; the lookup is scoped to the hovered entity's own model.
|
|
2780
|
+
const hovered = options.hoverOutline;
|
|
2781
|
+
const hoverId = hovered && !options.hiddenIds?.has(hovered.id)
|
|
2782
|
+
&& (!hasIsolatedFilter || options.isolatedIds.has(hovered.id)) ? hovered.id : null;
|
|
2783
|
+
const hoverModel = hovered?.modelIndex;
|
|
2784
|
+
const hoverPieces = hoverId != null && !selectedMeshesForMask.some((m) => matchesHoveredMesh(m, hoverId, hoverModel))
|
|
2785
|
+
? this.hoverMeshes.resolve(device, hoverId, hoverModel, () => this.scene.getMeshDataPieces(hoverId, hoverModel), (m) => this.individualMeshFrame(m))
|
|
2786
|
+
: (this.hoverMeshes.release(), []);
|
|
2787
|
+
const selectionOutline = buildSelectionOutlineFrame({
|
|
2788
|
+
uniformBufferSize: this.pipeline.getUniformBufferSize(), viewProj, relativeToEyeFrame,
|
|
2789
|
+
section: sectionPlaneData, sectionFlipped: options.sectionPlane?.flipped, clipBox: options.clipBox,
|
|
2790
|
+
selectedMeshes: selectedMeshesForMask, hoverPieces, hoveredId: hoverId, selectedModelIndex: hoverModel,
|
|
2791
|
+
instancedTemplates: this.scene.getInstancedTemplates(),
|
|
2792
|
+
instancedHovered: hoverId != null ? this.scene.getInstancedTemplatesOf(hoverId, hoverModel) : [],
|
|
2793
|
+
});
|
|
2825
2794
|
// Created lazily like the sky/shadow passes; each pass inside is too.
|
|
2826
|
-
this.postPasses ??= new PostPassChain(this.device, this.pipeline.getSampleCount());
|
|
2795
|
+
this.postPasses ??= new PostPassChain(this.device, this.pipeline.getSampleCount(), this.pipeline.getBindGroupLayout());
|
|
2827
2796
|
this.postPasses.encode({
|
|
2828
2797
|
encoder,
|
|
2829
2798
|
targetView: textureView,
|
|
@@ -2838,6 +2807,7 @@ export class Renderer {
|
|
|
2838
2807
|
pixelRatio: this.pixelRatio,
|
|
2839
2808
|
// EDL only when point clouds are loaded and the user enabled it.
|
|
2840
2809
|
edl: this.edlOptions.enabled && this.pointCloudRenderer?.hasAssets() ? this.edlOptions : null,
|
|
2810
|
+
selectionOutline,
|
|
2841
2811
|
});
|
|
2842
2812
|
colorReadback = colorCapture && encodeRendererColorFrameCapture(device, encoder, colorCapture);
|
|
2843
2813
|
device.queue.submit([encoder.finish()]);
|
|
@@ -2982,7 +2952,11 @@ export class Renderer {
|
|
|
2982
2952
|
* These are scaled internally to match the actual canvas pixel dimensions.
|
|
2983
2953
|
*/
|
|
2984
2954
|
raycastSceneMagnetic(x, y, currentEdgeLock, options) {
|
|
2985
|
-
return this.raycastEngine.raycastSceneMagnetic(x, y, currentEdgeLock, options);
|
|
2955
|
+
return this.raycastEngine.raycastSceneMagnetic(x, y, currentEdgeLock, options, this.activePickClip());
|
|
2956
|
+
}
|
|
2957
|
+
/** Opt in the currently selected authored curves for exact magnetic picking. */
|
|
2958
|
+
setSourceSnapCurves(curves) {
|
|
2959
|
+
this.raycastEngine.setSourceSnapCurves(curves);
|
|
2986
2960
|
}
|
|
2987
2961
|
/**
|
|
2988
2962
|
* Invalidate BVH cache (call when geometry changes)
|
|
@@ -3097,9 +3071,19 @@ export class Renderer {
|
|
|
3097
3071
|
* `SymbolicTextInput.color` on `uploadAnnotationTexts3D`.
|
|
3098
3072
|
*/
|
|
3099
3073
|
setOverlayTheme(theme) {
|
|
3074
|
+
const previous = this.overlayTheme;
|
|
3100
3075
|
this.overlayTheme = theme;
|
|
3101
3076
|
this.pipeline?.selectionColorUniform.update(theme.selection);
|
|
3102
3077
|
this.overlays.setTheme(theme);
|
|
3078
|
+
// Clash-pair tints already uploaded to the entity colour table must
|
|
3079
|
+
// follow a theme change as the other clash overlays do (#5490).
|
|
3080
|
+
const installed = this.scene.getColorOverrides();
|
|
3081
|
+
const repainted = installed && remapOverrideColors(installed, [[previous.clashA, theme.clashA], [previous.clashB, theme.clashB]]);
|
|
3082
|
+
const device = this.getGPUDevice();
|
|
3083
|
+
if (repainted && device && this.pipeline) {
|
|
3084
|
+
this.scene.setColorOverrides(repainted, device, this.pipeline);
|
|
3085
|
+
this.requestRender();
|
|
3086
|
+
}
|
|
3103
3087
|
}
|
|
3104
3088
|
/**
|
|
3105
3089
|
* Set one standalone 3D line overlay, or clear it by passing `null`.
|
|
@@ -3314,6 +3298,7 @@ export class Renderer {
|
|
|
3314
3298
|
this.pickingManager.setPicker(null);
|
|
3315
3299
|
this.postPasses?.destroy();
|
|
3316
3300
|
this.postPasses = null;
|
|
3301
|
+
this.hoverMeshes.release();
|
|
3317
3302
|
this.skyPass?.destroy();
|
|
3318
3303
|
this.skyPass = null;
|
|
3319
3304
|
this.shadowPass?.destroy();
|