@ifc-lite/renderer 6.3.3 → 6.5.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/camera-animation.d.ts +7 -1
- package/dist/camera-animation.d.ts.map +1 -1
- package/dist/camera-animation.js +19 -3
- package/dist/camera-animation.js.map +1 -1
- package/dist/camera-fit-geometry.d.ts +47 -0
- package/dist/camera-fit-geometry.d.ts.map +1 -0
- package/dist/camera-fit-geometry.js +44 -0
- package/dist/camera-fit-geometry.js.map +1 -0
- package/dist/camera-framing.d.ts +27 -21
- package/dist/camera-framing.d.ts.map +1 -1
- package/dist/camera-framing.js +47 -44
- package/dist/camera-framing.js.map +1 -1
- package/dist/camera-preset-view.d.ts +18 -0
- package/dist/camera-preset-view.d.ts.map +1 -1
- package/dist/camera-preset-view.js +42 -0
- package/dist/camera-preset-view.js.map +1 -1
- package/dist/camera.d.ts +14 -1
- package/dist/camera.d.ts.map +1 -1
- package/dist/camera.js +14 -2
- package/dist/camera.js.map +1 -1
- package/dist/clip-types.d.ts +35 -0
- package/dist/clip-types.d.ts.map +1 -0
- package/dist/clip-types.js +5 -0
- package/dist/clip-types.js.map +1 -0
- package/dist/device-recovery.js +11 -1
- package/dist/device-recovery.js.map +1 -1
- package/dist/device.d.ts +4 -4
- package/dist/device.js +9 -9
- package/dist/device.js.map +1 -1
- package/dist/extra-clip-planes.d.ts +57 -0
- package/dist/extra-clip-planes.d.ts.map +1 -0
- package/dist/extra-clip-planes.js +161 -0
- package/dist/extra-clip-planes.js.map +1 -0
- package/dist/gpu-counters.d.ts +4 -2
- package/dist/gpu-counters.d.ts.map +1 -1
- package/dist/gpu-counters.js +9 -6
- package/dist/gpu-counters.js.map +1 -1
- package/dist/index.d.ts +19 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +69 -90
- package/dist/index.js.map +1 -1
- package/dist/mesh-rte-uniforms.d.ts +7 -3
- package/dist/mesh-rte-uniforms.d.ts.map +1 -1
- package/dist/mesh-rte-uniforms.js +16 -2
- package/dist/mesh-rte-uniforms.js.map +1 -1
- package/dist/opaque-visibility-commands.d.ts +3 -0
- package/dist/opaque-visibility-commands.d.ts.map +1 -0
- package/dist/opaque-visibility-commands.js +11 -0
- package/dist/opaque-visibility-commands.js.map +1 -0
- package/dist/opaque-visibility-pass.d.ts +37 -0
- package/dist/opaque-visibility-pass.d.ts.map +1 -0
- package/dist/opaque-visibility-pass.js +230 -0
- package/dist/opaque-visibility-pass.js.map +1 -0
- package/dist/pick-types.d.ts +5 -1
- package/dist/pick-types.d.ts.map +1 -1
- package/dist/picker-rte-uniforms.d.ts +1 -1
- package/dist/picker-rte-uniforms.d.ts.map +1 -1
- package/dist/picker-rte-uniforms.js +4 -2
- package/dist/picker-rte-uniforms.js.map +1 -1
- package/dist/picker-shaders.d.ts +5 -0
- package/dist/picker-shaders.d.ts.map +1 -0
- package/dist/picker-shaders.js +157 -0
- package/dist/picker-shaders.js.map +1 -0
- package/dist/picker.d.ts +3 -0
- package/dist/picker.d.ts.map +1 -1
- package/dist/picker.js +43 -133
- package/dist/picker.js.map +1 -1
- package/dist/pipeline.d.ts +5 -9
- package/dist/pipeline.d.ts.map +1 -1
- package/dist/pipeline.js +34 -29
- package/dist/pipeline.js.map +1 -1
- package/dist/point-picker.d.ts +2 -1
- package/dist/point-picker.d.ts.map +1 -1
- package/dist/point-picker.js +12 -4
- package/dist/point-picker.js.map +1 -1
- package/dist/pointcloud/point-cloud-renderer.d.ts +4 -4
- package/dist/pointcloud/point-cloud-renderer.d.ts.map +1 -1
- package/dist/pointcloud/point-cloud-renderer.js +1 -1
- package/dist/pointcloud/point-cloud-renderer.js.map +1 -1
- package/dist/pointcloud/point-cloud-uniforms.d.ts +3 -1
- package/dist/pointcloud/point-cloud-uniforms.d.ts.map +1 -1
- package/dist/pointcloud/point-cloud-uniforms.js +3 -1
- package/dist/pointcloud/point-cloud-uniforms.js.map +1 -1
- package/dist/pointcloud/point-pipeline.d.ts +3 -1
- package/dist/pointcloud/point-pipeline.d.ts.map +1 -1
- package/dist/pointcloud/point-pipeline.js +4 -1
- package/dist/pointcloud/point-pipeline.js.map +1 -1
- package/dist/pointcloud/point-shader.wgsl.d.ts +1 -1
- package/dist/pointcloud/point-shader.wgsl.d.ts.map +1 -1
- package/dist/pointcloud/point-shader.wgsl.js +8 -0
- package/dist/pointcloud/point-shader.wgsl.js.map +1 -1
- package/dist/post-pass-chain.d.ts +1 -0
- package/dist/post-pass-chain.d.ts.map +1 -1
- package/dist/post-pass-chain.js +1 -0
- package/dist/post-pass-chain.js.map +1 -1
- package/dist/quantized-lifecycle.d.ts +6 -0
- package/dist/quantized-lifecycle.d.ts.map +1 -0
- package/dist/quantized-lifecycle.js +19 -0
- package/dist/quantized-lifecycle.js.map +1 -0
- package/dist/raycast-engine.d.ts.map +1 -1
- package/dist/raycast-engine.js +3 -2
- package/dist/raycast-engine.js.map +1 -1
- package/dist/render-section-draw.d.ts +5 -0
- package/dist/render-section-draw.d.ts.map +1 -1
- package/dist/render-section-draw.js +59 -25
- package/dist/render-section-draw.js.map +1 -1
- package/dist/render-section-plane.d.ts +5 -33
- package/dist/render-section-plane.d.ts.map +1 -1
- package/dist/render-section-plane.js +8 -175
- package/dist/render-section-plane.js.map +1 -1
- package/dist/renderer-frame-timing.d.ts +1 -0
- package/dist/renderer-frame-timing.d.ts.map +1 -1
- package/dist/renderer-frame-timing.js +8 -1
- package/dist/renderer-frame-timing.js.map +1 -1
- package/dist/renderer-overlays.d.ts +8 -2
- package/dist/renderer-overlays.d.ts.map +1 -1
- package/dist/renderer-overlays.js +22 -45
- package/dist/renderer-overlays.js.map +1 -1
- package/dist/scene-raycaster.d.ts +1 -1
- package/dist/scene-raycaster.d.ts.map +1 -1
- package/dist/scene-raycaster.js +18 -44
- package/dist/scene-raycaster.js.map +1 -1
- package/dist/scene-rect-select.d.ts.map +1 -1
- package/dist/scene-rect-select.js +2 -1
- package/dist/scene-rect-select.js.map +1 -1
- package/dist/section-2d-lift.d.ts +25 -0
- package/dist/section-2d-lift.d.ts.map +1 -1
- package/dist/section-2d-lift.js +35 -0
- package/dist/section-2d-lift.js.map +1 -1
- package/dist/section-2d-overlay.d.ts +41 -12
- package/dist/section-2d-overlay.d.ts.map +1 -1
- package/dist/section-2d-overlay.js +121 -179
- package/dist/section-2d-overlay.js.map +1 -1
- package/dist/section-2d-types.d.ts +2 -0
- package/dist/section-2d-types.d.ts.map +1 -1
- package/dist/section-cap-geometry.d.ts +33 -0
- package/dist/section-cap-geometry.d.ts.map +1 -0
- package/dist/section-cap-geometry.js +137 -0
- package/dist/section-cap-geometry.js.map +1 -0
- package/dist/section-cap-set.d.ts +60 -0
- package/dist/section-cap-set.d.ts.map +1 -0
- package/dist/section-cap-set.js +212 -0
- package/dist/section-cap-set.js.map +1 -0
- package/dist/section-cap-style.d.ts +3 -0
- package/dist/section-cap-style.d.ts.map +1 -1
- package/dist/section-cap-style.js.map +1 -1
- package/dist/section-cap-upload.d.ts +16 -0
- package/dist/section-cap-upload.d.ts.map +1 -0
- package/dist/section-cap-upload.js +52 -0
- package/dist/section-cap-upload.js.map +1 -0
- package/dist/section-exclusions.d.ts +26 -0
- package/dist/section-exclusions.d.ts.map +1 -0
- package/dist/section-exclusions.js +95 -0
- package/dist/section-exclusions.js.map +1 -0
- package/dist/section-plane-frames.d.ts +48 -0
- package/dist/section-plane-frames.d.ts.map +1 -0
- package/dist/section-plane-frames.js +49 -0
- package/dist/section-plane-frames.js.map +1 -0
- package/dist/section-plane-resolve.d.ts +63 -0
- package/dist/section-plane-resolve.d.ts.map +1 -0
- package/dist/section-plane-resolve.js +200 -0
- package/dist/section-plane-resolve.js.map +1 -0
- package/dist/selection-mask-pass.d.ts +2 -0
- package/dist/selection-mask-pass.d.ts.map +1 -1
- package/dist/selection-mask-pass.js +16 -4
- package/dist/selection-mask-pass.js.map +1 -1
- package/dist/selection-mask-pipelines.d.ts +5 -1
- package/dist/selection-mask-pipelines.d.ts.map +1 -1
- package/dist/selection-mask-pipelines.js +27 -3
- package/dist/selection-mask-pipelines.js.map +1 -1
- package/dist/selection-outline-frame.d.ts +3 -1
- package/dist/selection-outline-frame.d.ts.map +1 -1
- package/dist/selection-outline-frame.js +2 -1
- package/dist/selection-outline-frame.js.map +1 -1
- package/dist/shaders/depth-nudge.wgsl.d.ts +2 -2
- package/dist/shaders/depth-nudge.wgsl.d.ts.map +1 -1
- package/dist/shaders/depth-nudge.wgsl.js +1 -18
- package/dist/shaders/depth-nudge.wgsl.js.map +1 -1
- 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 +9 -1
- package/dist/shaders/main.wgsl.js.map +1 -1
- package/dist/shaders/mesh-uniforms.wgsl.d.ts +1 -1
- package/dist/shaders/mesh-uniforms.wgsl.d.ts.map +1 -1
- package/dist/shaders/mesh-uniforms.wgsl.js +11 -3
- package/dist/shaders/mesh-uniforms.wgsl.js.map +1 -1
- package/dist/shaders/opaque-visibility.wgsl.d.ts +11 -0
- package/dist/shaders/opaque-visibility.wgsl.d.ts.map +1 -0
- package/dist/shaders/opaque-visibility.wgsl.js +55 -0
- package/dist/shaders/opaque-visibility.wgsl.js.map +1 -0
- package/dist/shaders/section-2d-overlay.wgsl.d.ts +11 -5
- package/dist/shaders/section-2d-overlay.wgsl.d.ts.map +1 -1
- package/dist/shaders/section-2d-overlay.wgsl.js +31 -5
- package/dist/shaders/section-2d-overlay.wgsl.js.map +1 -1
- package/dist/shaders/selection-mask.wgsl.d.ts +1 -1
- package/dist/shaders/selection-mask.wgsl.d.ts.map +1 -1
- package/dist/shaders/selection-mask.wgsl.js +10 -4
- package/dist/shaders/selection-mask.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 +22 -7
- package/dist/shaders/shadow.wgsl.js.map +1 -1
- package/dist/shaders/symbolic-overlay.wgsl.d.ts +1 -1
- package/dist/shaders/symbolic-overlay.wgsl.d.ts.map +1 -1
- 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 +2 -5
- package/dist/shadow-pass.d.ts.map +1 -1
- package/dist/shadow-pass.js +17 -17
- package/dist/shadow-pass.js.map +1 -1
- package/dist/shadow-types.d.ts +7 -0
- package/dist/shadow-types.d.ts.map +1 -1
- package/dist/shadow-types.js.map +1 -1
- package/dist/source-curve-clipping.d.ts.map +1 -1
- package/dist/source-curve-clipping.js +7 -1
- package/dist/source-curve-clipping.js.map +1 -1
- package/dist/source-curve-snap.d.ts.map +1 -1
- package/dist/source-curve-snap.js +2 -1
- package/dist/source-curve-snap.js.map +1 -1
- package/dist/types.d.ts +12 -16
- package/dist/types.d.ts.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
import { depthNudgeWgsl } from './depth-nudge.wgsl.js';
|
|
5
|
+
/** Bind after mesh and environment/hover groups. Shared by colour and masks. */
|
|
6
|
+
export const OPAQUE_VISIBILITY_GROUP = 2;
|
|
7
|
+
/** Numerical coincidence allowance, independent of the entity's hash rank. */
|
|
8
|
+
export const OPAQUE_VISIBILITY_ULPS = 4;
|
|
9
|
+
/** depth32float permits interpolation roundoff, independent of the hash rank. */
|
|
10
|
+
export function opaqueVisibilityWgsl(samples) {
|
|
11
|
+
return `
|
|
12
|
+
@group(${OPAQUE_VISIBILITY_GROUP}) @binding(0) var geometricDepth: ${samples > 1 ? 'texture_depth_multisampled_2d' : 'texture_depth_2d'};
|
|
13
|
+
fn geometricallyOccluded(pixel: vec2<f32>, depth: f32, sampleIndex: u32) -> bool {
|
|
14
|
+
let nearest = textureLoad(geometricDepth, vec2<i32>(pixel), ${samples > 1 ? 'i32(sampleIndex)' : '0'});
|
|
15
|
+
let bits = bitcast<u32>(nearest);
|
|
16
|
+
let lower = bitcast<f32>(bits - min(bits, ${OPAQUE_VISIBILITY_ULPS}u));
|
|
17
|
+
return depth < lower;
|
|
18
|
+
}
|
|
19
|
+
`;
|
|
20
|
+
}
|
|
21
|
+
/** Same transformations and fragment discards, without entity displacement. */
|
|
22
|
+
export function unbiasedMeshShader(source, samples, entryPoint = 'fs_main') {
|
|
23
|
+
if (!source.includes(depthNudgeWgsl))
|
|
24
|
+
throw new Error('Mesh shader has no canonical depth nudge');
|
|
25
|
+
const unbiased = source.replace(depthNudgeWgsl, 'fn nudgedClipZ(clip: vec4<f32>, hash: u32, vp: mat4x4<f32>) -> f32 { return clip.z; }')
|
|
26
|
+
.replace('@location(8) @interpolate(linear)', '@location(8) @interpolate(linear, sample)');
|
|
27
|
+
const entry = unbiased.indexOf(`fn ${entryPoint}(input: VertexOutput)`);
|
|
28
|
+
const attribute = unbiased.lastIndexOf('@fragment', entry);
|
|
29
|
+
if (entry < 0 || attribute < 0)
|
|
30
|
+
throw new Error('Visibility prepass has no fragment eligibility entry');
|
|
31
|
+
// Invoke the real fragment for its discards, then store the SAME sampled
|
|
32
|
+
// interpolant colour/masks compare. Mixing fixed-function MSAA depth with
|
|
33
|
+
// shader-interpolated depth can compare different raster sample positions.
|
|
34
|
+
const eligibility = unbiased.slice(attribute + '@fragment'.length)
|
|
35
|
+
.replace(/(fn \w+\(input: VertexOutput\)\s*->)\s*@location\(0\)/, '$1');
|
|
36
|
+
return unbiased.slice(0, attribute) + eligibility + `
|
|
37
|
+
@fragment fn fs_geometric_depth(input: VertexOutput, @builtin(sample_index) sampleIndex: u32) -> @builtin(frag_depth) f32 {
|
|
38
|
+
let eligible = ${entryPoint}(input);
|
|
39
|
+
if (sampleIndex >= ${samples}u) { discard; }
|
|
40
|
+
return input.geometricDepth;
|
|
41
|
+
}
|
|
42
|
+
`;
|
|
43
|
+
}
|
|
44
|
+
/** Guard an actual colour fragment; keep its clipping, texture and opacity rules. */
|
|
45
|
+
export function guardedMeshShader(source, samples, entryPoint = 'fs_main') {
|
|
46
|
+
const anchor = `fn ${entryPoint}(input: VertexOutput)`;
|
|
47
|
+
if (!source.includes(anchor))
|
|
48
|
+
throw new Error(`Visibility fragment ${entryPoint} has no canonical input`);
|
|
49
|
+
const signature = source.replace(anchor, `fn ${entryPoint}(input: VertexOutput, @builtin(sample_index) sampleIndex: u32)`);
|
|
50
|
+
const start = signature.indexOf('{', signature.indexOf(`fn ${entryPoint}(`));
|
|
51
|
+
const guard = '\n if (geometricallyOccluded(input.position.xy, input.geometricDepth, sampleIndex)) { discard; }\n';
|
|
52
|
+
return (opaqueVisibilityWgsl(samples) + signature.slice(0, start + 1) + guard + signature.slice(start + 1))
|
|
53
|
+
.replace('@location(8) @interpolate(linear)', '@location(8) @interpolate(linear, sample)');
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=opaque-visibility.wgsl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opaque-visibility.wgsl.js","sourceRoot":"","sources":["../../src/shaders/opaque-visibility.wgsl.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,gFAAgF;AAChF,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC;AAEzC,8EAA8E;AAC9E,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC;AAExC,iFAAiF;AACjF,MAAM,UAAU,oBAAoB,CAAC,OAAe;IAClD,OAAO;aACI,uBAAuB,qCAAqC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,+BAA+B,CAAC,CAAC,CAAC,kBAAkB;;oEAEvE,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,GAAG;;kDAExD,sBAAsB;;;GAGrE,CAAC;AACJ,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,kBAAkB,CAAC,MAAc,EAAE,OAAe,EAAE,UAAU,GAAG,SAAS;IACxF,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;IAClG,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,cAAc,EAC5C,uFAAuF,CAAC;SACvF,OAAO,CAAC,mCAAmC,EAAE,2CAA2C,CAAC,CAAC;IAC7F,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,UAAU,uBAAuB,CAAC,CAAC;IACxE,MAAM,SAAS,GAAG,QAAQ,CAAC,WAAW,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;IAC3D,IAAI,KAAK,GAAG,CAAC,IAAI,SAAS,GAAG,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;IACxG,yEAAyE;IACzE,0EAA0E;IAC1E,2EAA2E;IAC3E,MAAM,WAAW,GAAG,QAAQ,CAAC,KAAK,CAAC,SAAS,GAAG,WAAW,CAAC,MAAM,CAAC;SAC/D,OAAO,CAAC,uDAAuD,EAAE,IAAI,CAAC,CAAC;IAC1E,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,GAAG,WAAW,GAAG;;uBAE/B,UAAU;2BACN,OAAO;;;GAG/B,CAAC;AACJ,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,iBAAiB,CAAC,MAAc,EAAE,OAAe,EAAE,UAAU,GAAG,SAAS;IACvF,MAAM,MAAM,GAAG,MAAM,UAAU,uBAAuB,CAAC;IACvD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,UAAU,yBAAyB,CAAC,CAAC;IAC1G,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,UAAU,gEAAgE,CAAC,CAAC;IAC3H,MAAM,KAAK,GAAG,SAAS,CAAC,OAAO,CAAC,GAAG,EAAE,SAAS,CAAC,OAAO,CAAC,MAAM,UAAU,GAAG,CAAC,CAAC,CAAC;IAC7E,MAAM,KAAK,GAAG,oGAAoG,CAAC;IACnH,OAAO,CAAC,oBAAoB,CAAC,OAAO,CAAC,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,GAAG,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;SACxG,OAAO,CAAC,mCAAmC,EAAE,2CAA2C,CAAC,CAAC;AAC/F,CAAC"}
|
|
@@ -72,8 +72,12 @@ export declare const SECTION_2D_UNIFORM_SLOT_INDEX: {
|
|
|
72
72
|
readonly terrain: number;
|
|
73
73
|
readonly centreline: number;
|
|
74
74
|
readonly clashBox: number;
|
|
75
|
+
/** The cut caps of the cutting planes beyond the first (#7545): one record per plane, after every line range. */
|
|
76
|
+
readonly extraCap: number;
|
|
75
77
|
};
|
|
76
|
-
/**
|
|
78
|
+
/** Caps of the planes beyond the first (`MAX_EXTRA_CLIP_PLANES`). */
|
|
79
|
+
export declare const SECTION_2D_MAX_EXTRA_CAPS = 7;
|
|
80
|
+
/** One cap record, a partition range for every line family, and a cap record per extra plane. */
|
|
77
81
|
export declare const SECTION_2D_UNIFORM_SLOT_COUNT: number;
|
|
78
82
|
/**
|
|
79
83
|
* Byte stride between uniform slots for `device`.
|
|
@@ -95,10 +99,12 @@ export declare function sectionUniformSlotStride(device: GPUDevice): number;
|
|
|
95
99
|
* cap style, so single-material cuts read as one architectural section exactly
|
|
96
100
|
* as before. Hatch + stroke apply over either base.
|
|
97
101
|
*/
|
|
98
|
-
export declare const SECTION_2D_CAP_FILL_WGSL = "\n struct Uniforms {\n viewProj: mat4x4<f32>,\n planeOffset: vec4<f32>, // Small offset to render slightly in front of section plane\n capFillColor: vec4<f32>,\n capStrokeColor: vec4<f32>,\n // x=patternId, y=spacingPx, z=angleRad, w=widthPx\n params: vec4<f32>,\n // x=secondaryAngleRad, y,z,w reserved\n params2: vec4<f32>,\n lineColor: vec4<f32>,\n rteViewProj: mat4x4<f32>,\n originDeltaHigh: vec4<f32>,\n originDeltaLow: vec4<f32>,\n }\n @binding(0) @group(0) var<uniform> uniforms: Uniforms;\n\n struct VertexInput {\n @location(0) position: vec3<f32>,\n @location(1) color: vec4<f32>,\n }\n\n struct VertexOutput {\n @builtin(position) position: vec4<f32>,\n @location(0) color: vec4<f32>,\n }\n\n @vertex\n fn vs_main(input: VertexInput) -> VertexOutput {\n var output: VertexOutput;\n let offsetPos = input.position + uniforms.planeOffset.xyz;\n let global = uniforms.viewProj * vec4<f32>(offsetPos, 1.0);\n let relative = (offsetPos + uniforms.originDeltaHigh.xyz) + uniforms.originDeltaLow.xyz;\n output.position = select(global, uniforms.rteViewProj * vec4<f32>(relative, 1.0), uniforms.originDeltaHigh.w == 1.0);\n output.color = input.color;\n return output;\n }\n\n // Screen-space hatch pattern helpers (ported from section-cap.wgsl).\n fn lineMask(u: f32, s: f32, w: f32) -> f32 {\n let f = fract(u / s) * s;\n let d = min(f, s - f);\n return 1.0 - smoothstep(w * 0.5, w * 0.5 + 1.0, d);\n }\n fn rotate(p: vec2<f32>, a: f32) -> vec2<f32> {\n let c = cos(a);\n let s = sin(a);\n return vec2<f32>(c * p.x - s * p.y, s * p.x + c * p.y);\n }\n fn hatchIntensity(fragCoord: vec2<f32>, patternId: u32, spacing: f32, angle: f32, width: f32, angle2: f32) -> f32 {\n let p = fragCoord;\n if (patternId == 0u) { return 0.0; } // solid\n if (patternId == 1u) { // diagonal\n let r = rotate(p, angle);\n return lineMask(r.x, spacing, width);\n }\n if (patternId == 2u) { // cross-hatch\n let r = rotate(p, angle);\n let r2 = rotate(p, angle2);\n return max(lineMask(r.x, spacing, width), lineMask(r2.x, spacing, width));\n }\n if (patternId == 3u) { return lineMask(p.y, spacing, width); } // horizontal\n if (patternId == 4u) { return lineMask(p.x, spacing, width); } // vertical\n if (patternId == 5u) {\n // Concrete (ISO 128-50): clean regular dot grid. The previous\n // version layered dashes on top which looked noisy and broken.\n // Dots sit at every grid intersection; radius scales with\n // stroke width so the user's width slider works consistently.\n let gx = p.x - round(p.x / spacing) * spacing;\n let gy = p.y - round(p.y / spacing) * spacing;\n let d = sqrt(gx * gx + gy * gy);\n let radius = max(1.0, width * 1.2);\n return 1.0 - smoothstep(radius, radius + 1.0, d);\n }\n if (patternId == 6u) { // brick\n let bandH = spacing;\n let band = floor(p.y / bandH);\n // Signed parity. p is @builtin(position) today, so it is a\n // framebuffer coordinate and never negative \u2014 but u32() of a\n // negative float is an out-of-range conversion in WGSL, and the\n // moment this hatch is fed anything but fragCoord (a world- or\n // drawing-space variant) the running bond would break across\n // y = 0 in a backend-dependent way. i32(-1) & 1 is 1, so the\n // parity alternates correctly in both directions.\n let offset = select(0.0, bandH, (i32(band) & 1) == 1);\n let horiz = lineMask(p.y, bandH, width);\n let vertPos = p.x + offset * 0.5;\n let vert = step(fract(vertPos / (bandH * 2.0)), 0.02);\n return max(horiz, vert);\n }\n if (patternId == 7u) { // insulation\n let y = spacing * 0.5 * sin(p.x * 6.2831853 / spacing) + p.y;\n return lineMask(y, spacing, width);\n }\n return 0.0;\n }\n\n struct FragOut {\n @location(0) color: vec4<f32>,\n @location(1) objectId: vec4<f32>,\n }\n\n @fragment\n fn fs_main(input: VertexOutput) -> FragOut {\n let patternId = u32(uniforms.params.x + 0.5);\n let spacing = max(2.0, uniforms.params.y);\n let angle = uniforms.params.z;\n let width = max(1.0, uniforms.params.w);\n let angle2 = uniforms.params2.x;\n\n let h = hatchIntensity(input.position.xy, patternId, spacing, angle, width, angle2);\n // Per-polygon colour (a material-layer slab fills with its own\n // IfcMaterial RGBA) overrides the uniform cap fill when present.\n // Polygons without a colour carry the sentinel alpha -1 and fall back\n // to the user's cap style, byte-identically. Hatch + stroke apply over\n // whichever base is chosen, so the architectural hatch still works.\n let useVertex = input.color.a >= 0.0;\n let baseFill = select(uniforms.capFillColor, input.color, useVertex);\n let rgb = mix(baseFill.rgb, uniforms.capStrokeColor.rgb, h * uniforms.capStrokeColor.a);\n let a = max(baseFill.a, h * uniforms.capStrokeColor.a);\n\n var out: FragOut;\n out.color = vec4<f32>(rgb, a);\n out.objectId = vec4<f32>(0.0, 0.0, 0.0, 0.0);\n return out;\n }\n ";
|
|
102
|
+
export declare const SECTION_2D_CAP_FILL_WGSL = "\n \n struct ExtraClip {\n planeInfo: vec4<u32>, // x = plane count, y = flipped bit mask\n planes: array<vec4<f32>, 7>, // xyz = normal, w = distance\n }\n\n // True when p is on the removed side of ANY extra plane.\n fn extraClipClipped(clip: ExtraClip, p: vec3<f32>) -> bool {\n for (var i = 0u; i < clip.planeInfo.x; i = i + 1u) {\n let side = select(1.0, -1.0, ((clip.planeInfo.y >> i) & 1u) == 1u);\n if ((dot(p, clip.planes[i].xyz) - clip.planes[i].w) * side > 0.0) { return true; }\n }\n return false;\n }\n\n struct Uniforms {\n viewProj: mat4x4<f32>,\n planeOffset: vec4<f32>, // Small offset to render slightly in front of section plane\n capFillColor: vec4<f32>,\n capStrokeColor: vec4<f32>,\n // x=patternId, y=spacingPx, z=angleRad, w=widthPx\n params: vec4<f32>,\n // x=secondaryAngleRad, y,z,w reserved\n params2: vec4<f32>,\n lineColor: vec4<f32>,\n rteViewProj: mat4x4<f32>,\n originDeltaHigh: vec4<f32>,\n originDeltaLow: vec4<f32>,\n }\n @binding(0) @group(0) var<uniform> uniforms: Uniforms;\n // The cutting planes that remove this cap's fragments (#7545): every other plane,\n // in the frame clipPos is expressed in. Count 0 = nothing to clip against.\n @binding(0) @group(1) var<uniform> extraClip: ExtraClip;\n\n struct VertexInput {\n @location(0) position: vec3<f32>,\n @location(1) color: vec4<f32>,\n }\n\n struct VertexOutput {\n @builtin(position) position: vec4<f32>,\n @location(0) color: vec4<f32>,\n @location(1) clipPos: vec3<f32>,\n }\n\n @vertex\n fn vs_main(input: VertexInput) -> VertexOutput {\n var output: VertexOutput;\n let offsetPos = input.position + uniforms.planeOffset.xyz;\n let global = uniforms.viewProj * vec4<f32>(offsetPos, 1.0);\n let relative = (offsetPos + uniforms.originDeltaHigh.xyz) + uniforms.originDeltaLow.xyz;\n output.position = select(global, uniforms.rteViewProj * vec4<f32>(relative, 1.0), uniforms.originDeltaHigh.w == 1.0);\n output.color = input.color;\n output.clipPos = relative;\n return output;\n }\n\n // Screen-space hatch pattern helpers (ported from section-cap.wgsl).\n fn lineMask(u: f32, s: f32, w: f32) -> f32 {\n let f = fract(u / s) * s;\n let d = min(f, s - f);\n return 1.0 - smoothstep(w * 0.5, w * 0.5 + 1.0, d);\n }\n fn rotate(p: vec2<f32>, a: f32) -> vec2<f32> {\n let c = cos(a);\n let s = sin(a);\n return vec2<f32>(c * p.x - s * p.y, s * p.x + c * p.y);\n }\n fn hatchIntensity(fragCoord: vec2<f32>, patternId: u32, spacing: f32, angle: f32, width: f32, angle2: f32) -> f32 {\n let p = fragCoord;\n if (patternId == 0u) { return 0.0; } // solid\n if (patternId == 1u) { // diagonal\n let r = rotate(p, angle);\n return lineMask(r.x, spacing, width);\n }\n if (patternId == 2u) { // cross-hatch\n let r = rotate(p, angle);\n let r2 = rotate(p, angle2);\n return max(lineMask(r.x, spacing, width), lineMask(r2.x, spacing, width));\n }\n if (patternId == 3u) { return lineMask(p.y, spacing, width); } // horizontal\n if (patternId == 4u) { return lineMask(p.x, spacing, width); } // vertical\n if (patternId == 5u) {\n // Concrete (ISO 128-50): clean regular dot grid. The previous\n // version layered dashes on top which looked noisy and broken.\n // Dots sit at every grid intersection; radius scales with\n // stroke width so the user's width slider works consistently.\n let gx = p.x - round(p.x / spacing) * spacing;\n let gy = p.y - round(p.y / spacing) * spacing;\n let d = sqrt(gx * gx + gy * gy);\n let radius = max(1.0, width * 1.2);\n return 1.0 - smoothstep(radius, radius + 1.0, d);\n }\n if (patternId == 6u) { // brick\n let bandH = spacing;\n let band = floor(p.y / bandH);\n // Signed parity. p is @builtin(position) today, so it is a\n // framebuffer coordinate and never negative \u2014 but u32() of a\n // negative float is an out-of-range conversion in WGSL, and the\n // moment this hatch is fed anything but fragCoord (a world- or\n // drawing-space variant) the running bond would break across\n // y = 0 in a backend-dependent way. i32(-1) & 1 is 1, so the\n // parity alternates correctly in both directions.\n let offset = select(0.0, bandH, (i32(band) & 1) == 1);\n let horiz = lineMask(p.y, bandH, width);\n let vertPos = p.x + offset * 0.5;\n let vert = step(fract(vertPos / (bandH * 2.0)), 0.02);\n return max(horiz, vert);\n }\n if (patternId == 7u) { // insulation\n let y = spacing * 0.5 * sin(p.x * 6.2831853 / spacing) + p.y;\n return lineMask(y, spacing, width);\n }\n return 0.0;\n }\n\n struct FragOut {\n @location(0) color: vec4<f32>,\n @location(1) objectId: vec4<f32>,\n }\n\n @fragment\n fn fs_main(input: VertexOutput) -> FragOut {\n if (extraClipClipped(extraClip, input.clipPos)) { discard; }\n let patternId = u32(uniforms.params.x + 0.5);\n let spacing = max(2.0, uniforms.params.y);\n let angle = uniforms.params.z;\n let width = max(1.0, uniforms.params.w);\n let angle2 = uniforms.params2.x;\n\n let h = hatchIntensity(input.position.xy, patternId, spacing, angle, width, angle2);\n // Per-polygon colour (a material-layer slab fills with its own\n // IfcMaterial RGBA) overrides the uniform cap fill when present.\n // Polygons without a colour carry the sentinel alpha -1 and fall back\n // to the user's cap style, byte-identically. Hatch + stroke apply over\n // whichever base is chosen, so the architectural hatch still works.\n let useVertex = input.color.a >= 0.0;\n let baseFill = select(uniforms.capFillColor, input.color, useVertex);\n let rgb = mix(baseFill.rgb, uniforms.capStrokeColor.rgb, h * uniforms.capStrokeColor.a);\n let a = max(baseFill.a, h * uniforms.capStrokeColor.a);\n\n var out: FragOut;\n out.color = vec4<f32>(rgb, a);\n out.objectId = vec4<f32>(0.0, 0.0, 0.0, 0.0);\n return out;\n }\n ";
|
|
103
|
+
export declare const SECTION_2D_OVERLAY_LINE_WGSL: string;
|
|
99
104
|
/**
|
|
100
|
-
*
|
|
101
|
-
*
|
|
105
|
+
* The section-cut outline's line shader, clipped like the cap fill it rides on:
|
|
106
|
+
* an outline must vanish where another cutting plane (#7545) removes its cap.
|
|
107
|
+
* The world-space line families keep the unclipped shader above.
|
|
102
108
|
*/
|
|
103
|
-
export declare const
|
|
109
|
+
export declare const SECTION_2D_CAP_LINE_WGSL: string;
|
|
104
110
|
//# sourceMappingURL=section-2d-overlay.wgsl.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"section-2d-overlay.wgsl.d.ts","sourceRoot":"","sources":["../../src/shaders/section-2d-overlay.wgsl.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"section-2d-overlay.wgsl.d.ts","sourceRoot":"","sources":["../../src/shaders/section-2d-overlay.wgsl.ts"],"names":[],"mappings":"AAuBA;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB;IACnC,+CAA+C;;IAE/C,+CAA+C;;IAE/C,+CAA+C;;IAE/C,gDAAgD;;IAEhD,gFAAgF;;IAEhF,oEAAoE;;IAEpE,mFAAmF;;IAEnF,oEAAoE;;IAEpE,8DAA8D;;;CAGtD,CAAC;AAEX,uEAAuE;AACvE,eAAO,MAAM,yBAAyB,KAAK,CAAC;AAE5C,6CAA6C;AAC7C,eAAO,MAAM,wBAAwB,QAAgC,CAAC;AAEtE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH;;;;;;;;GAQG;AACH,eAAO,MAAM,8BAA8B,MAAM,CAAC;AAElD,eAAO,MAAM,6BAA6B;IACxC,6EAA6E;;;;;;;;;IAS7E,iHAAiH;;CAEzG,CAAC;AAEX,qEAAqE;AACrE,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAE3C,iGAAiG;AACjG,eAAO,MAAM,6BAA6B,QAAqE,CAAC;AAEhH;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAIlE;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,wBAAwB,i8NAqI9B,CAAC;AAuER,eAAO,MAAM,4BAA4B,QAAwB,CAAC;AAElE;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,QAAuB,CAAC"}
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
* out of these sources and fails if either drifts from the slot table.
|
|
18
18
|
*/
|
|
19
19
|
import { overlayDepthLiftWgsl } from './depth-nudge.wgsl.js';
|
|
20
|
+
import { extraClipWgsl } from '../extra-clip-planes.js';
|
|
20
21
|
/**
|
|
21
22
|
* Float32 indices of each field in the shared uniform buffer.
|
|
22
23
|
*
|
|
@@ -91,9 +92,13 @@ export const SECTION_2D_UNIFORM_SLOT_INDEX = {
|
|
|
91
92
|
terrain: 1 + SECTION_2D_MAX_LINE_PARTITIONS * 4,
|
|
92
93
|
centreline: 1 + SECTION_2D_MAX_LINE_PARTITIONS * 5,
|
|
93
94
|
clashBox: 1 + SECTION_2D_MAX_LINE_PARTITIONS * 6,
|
|
95
|
+
/** The cut caps of the cutting planes beyond the first (#7545): one record per plane, after every line range. */
|
|
96
|
+
extraCap: 1 + SECTION_2D_MAX_LINE_PARTITIONS * 7,
|
|
94
97
|
};
|
|
95
|
-
/**
|
|
96
|
-
export const
|
|
98
|
+
/** Caps of the planes beyond the first (`MAX_EXTRA_CLIP_PLANES`). */
|
|
99
|
+
export const SECTION_2D_MAX_EXTRA_CAPS = 7;
|
|
100
|
+
/** One cap record, a partition range for every line family, and a cap record per extra plane. */
|
|
101
|
+
export const SECTION_2D_UNIFORM_SLOT_COUNT = 1 + SECTION_2D_MAX_LINE_PARTITIONS * 7 + SECTION_2D_MAX_EXTRA_CAPS;
|
|
97
102
|
/**
|
|
98
103
|
* Byte stride between uniform slots for `device`.
|
|
99
104
|
*
|
|
@@ -119,6 +124,7 @@ export function sectionUniformSlotStride(device) {
|
|
|
119
124
|
* as before. Hatch + stroke apply over either base.
|
|
120
125
|
*/
|
|
121
126
|
export const SECTION_2D_CAP_FILL_WGSL = /* wgsl */ `
|
|
127
|
+
${extraClipWgsl}
|
|
122
128
|
struct Uniforms {
|
|
123
129
|
viewProj: mat4x4<f32>,
|
|
124
130
|
planeOffset: vec4<f32>, // Small offset to render slightly in front of section plane
|
|
@@ -134,6 +140,9 @@ export const SECTION_2D_CAP_FILL_WGSL = /* wgsl */ `
|
|
|
134
140
|
originDeltaLow: vec4<f32>,
|
|
135
141
|
}
|
|
136
142
|
@binding(0) @group(0) var<uniform> uniforms: Uniforms;
|
|
143
|
+
// The cutting planes that remove this cap's fragments (#7545): every other plane,
|
|
144
|
+
// in the frame clipPos is expressed in. Count 0 = nothing to clip against.
|
|
145
|
+
@binding(0) @group(1) var<uniform> extraClip: ExtraClip;
|
|
137
146
|
|
|
138
147
|
struct VertexInput {
|
|
139
148
|
@location(0) position: vec3<f32>,
|
|
@@ -143,6 +152,7 @@ export const SECTION_2D_CAP_FILL_WGSL = /* wgsl */ `
|
|
|
143
152
|
struct VertexOutput {
|
|
144
153
|
@builtin(position) position: vec4<f32>,
|
|
145
154
|
@location(0) color: vec4<f32>,
|
|
155
|
+
@location(1) clipPos: vec3<f32>,
|
|
146
156
|
}
|
|
147
157
|
|
|
148
158
|
@vertex
|
|
@@ -153,6 +163,7 @@ export const SECTION_2D_CAP_FILL_WGSL = /* wgsl */ `
|
|
|
153
163
|
let relative = (offsetPos + uniforms.originDeltaHigh.xyz) + uniforms.originDeltaLow.xyz;
|
|
154
164
|
output.position = select(global, uniforms.rteViewProj * vec4<f32>(relative, 1.0), uniforms.originDeltaHigh.w == 1.0);
|
|
155
165
|
output.color = input.color;
|
|
166
|
+
output.clipPos = relative;
|
|
156
167
|
return output;
|
|
157
168
|
}
|
|
158
169
|
|
|
@@ -222,6 +233,7 @@ export const SECTION_2D_CAP_FILL_WGSL = /* wgsl */ `
|
|
|
222
233
|
|
|
223
234
|
@fragment
|
|
224
235
|
fn fs_main(input: VertexOutput) -> FragOut {
|
|
236
|
+
if (extraClipClipped(extraClip, input.clipPos)) { discard; }
|
|
225
237
|
let patternId = u32(uniforms.params.x + 0.5);
|
|
226
238
|
let spacing = max(2.0, uniforms.params.y);
|
|
227
239
|
let angle = uniforms.params.z;
|
|
@@ -249,7 +261,8 @@ export const SECTION_2D_CAP_FILL_WGSL = /* wgsl */ `
|
|
|
249
261
|
* Line shader for the section-cut outline and the standalone world-space line
|
|
250
262
|
* overlays (annotation / alignment / grid / DXF / clash box).
|
|
251
263
|
*/
|
|
252
|
-
|
|
264
|
+
function lineShaderWgsl(clipped) {
|
|
265
|
+
return /* wgsl */ `
|
|
253
266
|
// Mirrors the fill shader's uniform layout so both pipelines can share
|
|
254
267
|
// one buffer; the line shader only reads viewProj, planeOffset and the
|
|
255
268
|
// appended lineColor (byte offset 144). capFillColor/… are unused here but
|
|
@@ -267,6 +280,8 @@ export const SECTION_2D_OVERLAY_LINE_WGSL = /* wgsl */ `
|
|
|
267
280
|
originDeltaLow: vec4<f32>,
|
|
268
281
|
}
|
|
269
282
|
@binding(0) @group(0) var<uniform> uniforms: Uniforms;
|
|
283
|
+
${clipped ? `${extraClipWgsl}
|
|
284
|
+
@binding(0) @group(1) var<uniform> extraClip: ExtraClip;` : ''}
|
|
270
285
|
${overlayDepthLiftWgsl}
|
|
271
286
|
|
|
272
287
|
struct VertexInput {
|
|
@@ -274,7 +289,8 @@ export const SECTION_2D_OVERLAY_LINE_WGSL = /* wgsl */ `
|
|
|
274
289
|
}
|
|
275
290
|
|
|
276
291
|
struct VertexOutput {
|
|
277
|
-
@builtin(position) position: vec4<f32
|
|
292
|
+
@builtin(position) position: vec4<f32>,${clipped ? `
|
|
293
|
+
@location(0) clipPos: vec3<f32>,` : ''}
|
|
278
294
|
}
|
|
279
295
|
|
|
280
296
|
@vertex
|
|
@@ -289,7 +305,8 @@ export const SECTION_2D_OVERLAY_LINE_WGSL = /* wgsl */ `
|
|
|
289
305
|
// triangle topologies, so we do the equivalent in clip space —
|
|
290
306
|
// enough to beat MSAA jitter and the mesh depth nudge on annotation
|
|
291
307
|
// lines that ride exactly on a wall/floor.
|
|
292
|
-
output.position = vec4<f32>(clip.x, clip.y, overlayLiftedClipZ(clip, uniforms.viewProj), clip.w)
|
|
308
|
+
output.position = vec4<f32>(clip.x, clip.y, overlayLiftedClipZ(clip, uniforms.viewProj), clip.w);${clipped ? `
|
|
309
|
+
output.clipPos = relative;` : ''}
|
|
293
310
|
return output;
|
|
294
311
|
}
|
|
295
312
|
|
|
@@ -300,10 +317,19 @@ export const SECTION_2D_OVERLAY_LINE_WGSL = /* wgsl */ `
|
|
|
300
317
|
|
|
301
318
|
@fragment
|
|
302
319
|
fn fs_main(input: VertexOutput) -> FragOutLine {
|
|
320
|
+
${clipped ? 'if (extraClipClipped(extraClip, input.clipPos)) { discard; }' : ''}
|
|
303
321
|
var out: FragOutLine;
|
|
304
322
|
out.color = uniforms.lineColor; // consumer-themeable (defaults black)
|
|
305
323
|
out.objectId = vec4<f32>(0.0, 0.0, 0.0, 0.0);
|
|
306
324
|
return out;
|
|
307
325
|
}
|
|
308
326
|
`;
|
|
327
|
+
}
|
|
328
|
+
export const SECTION_2D_OVERLAY_LINE_WGSL = lineShaderWgsl(false);
|
|
329
|
+
/**
|
|
330
|
+
* The section-cut outline's line shader, clipped like the cap fill it rides on:
|
|
331
|
+
* an outline must vanish where another cutting plane (#7545) removes its cap.
|
|
332
|
+
* The world-space line families keep the unclipped shader above.
|
|
333
|
+
*/
|
|
334
|
+
export const SECTION_2D_CAP_LINE_WGSL = lineShaderWgsl(true);
|
|
309
335
|
//# sourceMappingURL=section-2d-overlay.wgsl.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"section-2d-overlay.wgsl.js","sourceRoot":"","sources":["../../src/shaders/section-2d-overlay.wgsl.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"section-2d-overlay.wgsl.js","sourceRoot":"","sources":["../../src/shaders/section-2d-overlay.wgsl.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAExD;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACtC,+CAA+C;IAC/C,QAAQ,EAAE,CAAC;IACX,+CAA+C;IAC/C,WAAW,EAAE,EAAE;IACf,+CAA+C;IAC/C,YAAY,EAAE,EAAE;IAChB,gDAAgD;IAChD,cAAc,EAAE,EAAE;IAClB,gFAAgF;IAChF,MAAM,EAAE,EAAE;IACV,oEAAoE;IACpE,OAAO,EAAE,EAAE;IACX,mFAAmF;IACnF,SAAS,EAAE,EAAE;IACb,oEAAoE;IACpE,WAAW,EAAE,EAAE;IACf,8DAA8D;IAC9D,eAAe,EAAE,EAAE;IACnB,cAAc,EAAE,EAAE;CACV,CAAC;AAEX,uEAAuE;AACvE,MAAM,CAAC,MAAM,yBAAyB,GAAG,EAAE,CAAC;AAE5C,6CAA6C;AAC7C,MAAM,CAAC,MAAM,wBAAwB,GAAG,yBAAyB,GAAG,CAAC,CAAC;AAEtE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,8BAA8B,GAAG,GAAG,CAAC;AAElD,MAAM,CAAC,MAAM,6BAA6B,GAAG;IAC3C,6EAA6E;IAC7E,UAAU,EAAE,CAAC;IACb,UAAU,EAAE,CAAC;IACb,SAAS,EAAE,CAAC,GAAG,8BAA8B;IAC7C,IAAI,EAAE,CAAC,GAAG,8BAA8B,GAAG,CAAC;IAC5C,GAAG,EAAE,CAAC,GAAG,8BAA8B,GAAG,CAAC;IAC3C,OAAO,EAAE,CAAC,GAAG,8BAA8B,GAAG,CAAC;IAC/C,UAAU,EAAE,CAAC,GAAG,8BAA8B,GAAG,CAAC;IAClD,QAAQ,EAAE,CAAC,GAAG,8BAA8B,GAAG,CAAC;IAChD,iHAAiH;IACjH,QAAQ,EAAE,CAAC,GAAG,8BAA8B,GAAG,CAAC;CACxC,CAAC;AAEX,qEAAqE;AACrE,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC;AAE3C,iGAAiG;AACjG,MAAM,CAAC,MAAM,6BAA6B,GAAG,CAAC,GAAG,8BAA8B,GAAG,CAAC,GAAG,yBAAyB,CAAC;AAEhH;;;;;;;GAOG;AACH,MAAM,UAAU,wBAAwB,CAAC,MAAiB;IACxD,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,+BAA+B,IAAI,GAAG,CAAC;IACxE,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC;IAC3E,OAAO,IAAI,CAAC,IAAI,CAAC,wBAAwB,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;AAC3D,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,UAAU,CAAC;UACzC,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAoIhB,CAAC;AAER;;;GAGG;AACH,SAAS,cAAc,CAAC,OAAgB;IACtC,OAAO,UAAU,CAAC;;;;;;;;;;;;;;;;;;UAkBV,OAAO,CAAC,CAAC,CAAC,GAAG,aAAa;iEAC6B,CAAC,CAAC,CAAC,EAAE;UAC5D,oBAAoB;;;;;;;mDAOqB,OAAO,CAAC,CAAC,CAAC;2CAClB,CAAC,CAAC,CAAC,EAAE;;;;;;;;;;;;;;;6GAe6D,OAAO,CAAC,CAAC,CAAC;qCAClF,CAAC,CAAC,CAAC,EAAE;;;;;;;;;;;YAW9B,OAAO,CAAC,CAAC,CAAC,8DAA8D,CAAC,CAAC,CAAC,EAAE;;;;;;OAMlF,CAAC;AACR,CAAC;AAED,MAAM,CAAC,MAAM,4BAA4B,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;AAElE;;;;GAIG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC"}
|
|
@@ -4,5 +4,5 @@
|
|
|
4
4
|
* selects its meshes on the CPU and declares no such binding.
|
|
5
5
|
*/
|
|
6
6
|
export declare const SELECTION_MASK_HOVER_GROUP = 1;
|
|
7
|
-
export declare function selectionMaskFragmentSource(instanced?: boolean): string;
|
|
7
|
+
export declare function selectionMaskFragmentSource(instanced?: boolean, samples?: number): string;
|
|
8
8
|
//# sourceMappingURL=selection-mask.wgsl.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"selection-mask.wgsl.d.ts","sourceRoot":"","sources":["../../src/shaders/selection-mask.wgsl.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"selection-mask.wgsl.d.ts","sourceRoot":"","sources":["../../src/shaders/selection-mask.wgsl.ts"],"names":[],"mappings":"AAyCA;;;;GAIG;AACH,eAAO,MAAM,0BAA0B,IAAI,CAAC;AAuB5C,wBAAgB,2BAA2B,CAAC,SAAS,UAAQ,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CA4CvF"}
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
* the depth test are the same code as the non-instanced variant.
|
|
37
37
|
*/
|
|
38
38
|
import { meshUniformsWgsl } from './mesh-uniforms.wgsl.js';
|
|
39
|
+
import { opaqueVisibilityWgsl } from './opaque-visibility.wgsl.js';
|
|
39
40
|
/**
|
|
40
41
|
* Bind-group index of the hovered-entity uniform (`vec4<u32>`, x = id) in the
|
|
41
42
|
* INSTANCED mask pipeline layout only (#5745). The non-instanced variant
|
|
@@ -62,7 +63,7 @@ function occurrenceFilterWgsl(instanced) {
|
|
|
62
63
|
return (input.instSelected & 2u) == 0u && input.entityId == maskHover.x;
|
|
63
64
|
}`;
|
|
64
65
|
}
|
|
65
|
-
export function selectionMaskFragmentSource(instanced = false) {
|
|
66
|
+
export function selectionMaskFragmentSource(instanced = false, samples) {
|
|
66
67
|
const instanceVaryings = instanced
|
|
67
68
|
? `
|
|
68
69
|
@location(2) @interpolate(flat) entityId: u32,
|
|
@@ -70,28 +71,33 @@ export function selectionMaskFragmentSource(instanced = false) {
|
|
|
70
71
|
: '';
|
|
71
72
|
return `
|
|
72
73
|
${meshUniformsWgsl}
|
|
74
|
+
${samples === undefined ? '' : opaqueVisibilityWgsl(samples)}
|
|
73
75
|
|
|
74
76
|
// The subset of the vertex stage's VertexOutput the mask reads (locations match).
|
|
75
77
|
struct MaskInput {
|
|
76
78
|
@builtin(position) fragPos: vec4<f32>,
|
|
77
79
|
@location(0) worldPos: vec3<f32>,
|
|
80
|
+
@location(9) @interpolate(flat) sectionEntityId: u32,
|
|
78
81
|
@location(6) eyePos: vec3<f32>,${instanceVaryings}
|
|
82
|
+
${samples === undefined ? '' : '@location(8) @interpolate(linear, sample) geometricDepth: f32,'}
|
|
79
83
|
}
|
|
80
84
|
${occurrenceFilterWgsl(instanced)}
|
|
81
85
|
|
|
82
86
|
fn isCut(input: MaskInput) -> bool {
|
|
83
|
-
return sectionClipped(clipSpacePos(input.worldPos, input.eyePos));
|
|
87
|
+
return sectionClipped(clipSpacePos(input.worldPos, input.eyePos), input.sectionEntityId);
|
|
84
88
|
}
|
|
85
89
|
|
|
86
90
|
@fragment
|
|
87
|
-
fn fs_mask_selected_visible(input: MaskInput) -> @location(0) vec4<f32> {
|
|
91
|
+
fn fs_mask_selected_visible(input: MaskInput${samples === undefined ? '' : ', @builtin(sample_index) sampleIndex: u32'}) -> @location(0) vec4<f32> {
|
|
88
92
|
if (!isSelectedOccurrence(input) || isCut(input)) { discard; }
|
|
93
|
+
${samples === undefined ? '' : 'if (geometricallyOccluded(input.fragPos.xy, input.geometricDepth, sampleIndex)) { discard; }'}
|
|
89
94
|
return vec4<f32>(1.0, 0.0, 0.0, 0.0);
|
|
90
95
|
}
|
|
91
96
|
|
|
92
97
|
@fragment
|
|
93
|
-
fn fs_mask_hover_visible(input: MaskInput) -> @location(0) vec4<f32> {
|
|
98
|
+
fn fs_mask_hover_visible(input: MaskInput${samples === undefined ? '' : ', @builtin(sample_index) sampleIndex: u32'}) -> @location(0) vec4<f32> {
|
|
94
99
|
if (!isHoveredOccurrence(input) || isCut(input)) { discard; }
|
|
100
|
+
${samples === undefined ? '' : 'if (geometricallyOccluded(input.fragPos.xy, input.geometricDepth, sampleIndex)) { discard; }'}
|
|
95
101
|
return vec4<f32>(0.0, 1.0, 0.0, 0.0);
|
|
96
102
|
}
|
|
97
103
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"selection-mask.wgsl.js","sourceRoot":"","sources":["../../src/shaders/selection-mask.wgsl.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"selection-mask.wgsl.js","sourceRoot":"","sources":["../../src/shaders/selection-mask.wgsl.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAEnE;;;;GAIG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,CAAC;AAE5C;;;;;GAKG;AACH,SAAS,oBAAoB,CAAC,SAAkB;IAC9C,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO;;0EAE+D,CAAC;IACzE,CAAC;IACD,OAAO;iBACQ,0BAA0B;;;;;UAKjC,CAAC;AACX,CAAC;AAED,MAAM,UAAU,2BAA2B,CAAC,SAAS,GAAG,KAAK,EAAE,OAAgB;IAC7E,MAAM,gBAAgB,GAAG,SAAS;QAChC,CAAC,CAAC;;6DAEuD;QACzD,CAAC,CAAC,EAAE,CAAC;IACP,OAAO;UACC,gBAAgB;UAChB,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,oBAAoB,CAAC,OAAO,CAAC;;;;;;;2CAOzB,gBAAgB;YAC/C,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,gEAAgE;;UAE/F,oBAAoB,CAAC,SAAS,CAAC;;;;;;;sDAOa,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,2CAA2C;;YAElH,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,8FAA8F;;;;;mDAKpF,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,2CAA2C;;YAE/G,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,8FAA8F;;;;;;;;;CAStI,CAAC;AACF,CAAC"}
|
|
@@ -27,5 +27,5 @@
|
|
|
27
27
|
* the unused `worldPos` output on those pipelines costs an interpolator, not a
|
|
28
28
|
* shader stage.
|
|
29
29
|
*/
|
|
30
|
-
export declare const shadowShaderSource = "\n \nstruct RteFrameUniform {\n // Camera translation is intentionally absent. This projects positions\n // returned by rteWorldPosition, which are already relative to the eye.\n viewProj: mat4x4<f32>,\n}\n\nstruct RteDrawableUniform {\n // CPU computes (drawable origin - camera origin) in f64 and splits it.\n // This data must be refreshed whenever the RTE camera frame changes.\n drawableDeltaHigh: vec4<f32>,\n drawableDeltaLow: vec4<f32>,\n}\n\nfn rteWorldPosition(\n local: vec3<f32>,\n drawable: RteDrawableUniform,\n) -> vec4<f32> {\n // Do not subtract a camera origin here: this delta was formed in CPU f64.\n let highDelta = drawable.drawableDeltaHigh.xyz;\n let lowDelta = drawable.drawableDeltaLow.xyz;\n // A drawable template can span kilometres. Keep local with high first;\n // The local-plus-(high-plus-low) association is not equivalent in f32.\n return vec4<f32>((local + highDelta) + lowDelta, 1.0);\n}\n\n struct Light {\n lightViewProj: mat4x4<f32>,\n cameraHigh: vec4<f32>,\n cameraLow: vec4<f32>,\n }\n @binding(0) @group(0) var<uniform> light: Light;\n\n struct Draw {\n model: mat4x4<f32>,\n // xyz = lattice-aligned quantMin (batch-origin-relative), w = step.\n // Read only by vs_shadow_quantized; zero for the other paths.\n quantParams: vec4<f32>,\n originHigh: vec4<f32>,\n originLow: vec4<f32>,\n }\n @binding(1) @group(0) var<uniform> draw: Draw;\n\n struct Clip {\n // xyz = plane normal, w = plane distance (world space).\n sectionPlane: vec4<f32>,\n clipBoxMin: vec4<f32>,\n clipBoxMax: vec4<f32>,\n // x packs: bit 0 = section enabled, bit 1 = section flipped,\n // bit 2 = clip box enabled. Mirrors main.wgsl's flags.y.\n flags: vec4<u32>,\n }\n @binding(2) @group(0) var<uniform> clip: Clip;\n\n struct FlatIn {\n @location(0) position: vec3<f32>,\n }\n\n struct QuantIn {\n @location(0) q: vec4<u32>, // uint16x4: lattice x, y, z, packedOct\n }\n\n struct InstanceIn {\n @location(3) m0: vec4<f32>,\n @location(4) m1: vec4<f32>,\n @location(5) m2: vec4<f32>,\n @location(6) m3: vec4<f32>,\n // Per-occurrence flags lane (bit 1 = hidden), same instance buffer the\n // colour pass reads. Locations 7 (entityId) and 8 (rgba) are in the\n // shared layout but unused by the depth pass.\n @location(9) flags: u32,\n // Split drawable-minus-camera delta from the slot-2 delta stream.\n @location(10) anchorHigh: vec4<f32>,\n @location(11) anchorLow: vec4<f32>,\n }\n\n struct ShadowOut {\n @builtin(position) position: vec4<f32>,\n @location(0) worldPos: vec3<f32>,\n }\n\n fn emit(eyePos: vec4<f32
|
|
30
|
+
export declare const shadowShaderSource = "\n \nstruct RteFrameUniform {\n // Camera translation is intentionally absent. This projects positions\n // returned by rteWorldPosition, which are already relative to the eye.\n viewProj: mat4x4<f32>,\n}\n\nstruct RteDrawableUniform {\n // CPU computes (drawable origin - camera origin) in f64 and splits it.\n // This data must be refreshed whenever the RTE camera frame changes.\n drawableDeltaHigh: vec4<f32>,\n drawableDeltaLow: vec4<f32>,\n}\n\nfn rteWorldPosition(\n local: vec3<f32>,\n drawable: RteDrawableUniform,\n) -> vec4<f32> {\n // Do not subtract a camera origin here: this delta was formed in CPU f64.\n let highDelta = drawable.drawableDeltaHigh.xyz;\n let lowDelta = drawable.drawableDeltaLow.xyz;\n // A drawable template can span kilometres. Keep local with high first;\n // The local-plus-(high-plus-low) association is not equivalent in f32.\n return vec4<f32>((local + highDelta) + lowDelta, 1.0);\n}\n\n\n struct ExtraClip {\n planeInfo: vec4<u32>, // x = plane count, y = flipped bit mask\n planes: array<vec4<f32>, 7>, // xyz = normal, w = distance\n }\n\n // True when p is on the removed side of ANY extra plane.\n fn extraClipClipped(clip: ExtraClip, p: vec3<f32>) -> bool {\n for (var i = 0u; i < clip.planeInfo.x; i = i + 1u) {\n let side = select(1.0, -1.0, ((clip.planeInfo.y >> i) & 1u) == 1u);\n if ((dot(p, clip.planes[i].xyz) - clip.planes[i].w) * side > 0.0) { return true; }\n }\n return false;\n }\n\n\n@group(3) @binding(0) var<storage, read> sectionExcludedIds: array<u32>;\n// Scene colour buckets are partitioned by 2^24 ID page. Reject an invalid anchor.\nfn sectionEntityFromLane(anchor: u32, lane: u32) -> u32 {\n return select(anchor + ((lane - anchor) & 0x00FFFFFFu), 0xFFFFFFFFu, anchor == 0xFFFFFFFFu);\n}\nfn sectionExcluded(id: u32) -> bool {\n var lo = 0u;\n var hi = sectionExcludedIds[0];\n while (lo < hi) {\n let mid = lo + (hi - lo) / 2u;\n let key = sectionExcludedIds[mid + 1u];\n if (key == id) { return true; }\n if (key < id) { lo = mid + 1u; } else { hi = mid; }\n }\n return false;\n}\n\n struct Light {\n lightViewProj: mat4x4<f32>,\n cameraHigh: vec4<f32>,\n cameraLow: vec4<f32>,\n }\n @binding(0) @group(0) var<uniform> light: Light;\n\n struct Draw {\n model: mat4x4<f32>,\n // xyz = lattice-aligned quantMin (batch-origin-relative), w = step.\n // Read only by vs_shadow_quantized; zero for the other paths.\n quantParams: vec4<f32>,\n originHigh: vec4<f32>,\n originLow: vec4<f32>,\n entity: vec4<u32>,\n }\n @binding(1) @group(0) var<uniform> draw: Draw;\n\n struct Clip {\n // xyz = plane normal, w = plane distance (world space).\n sectionPlane: vec4<f32>,\n clipBoxMin: vec4<f32>,\n clipBoxMax: vec4<f32>,\n // x packs: bit 0 = section enabled, bit 1 = section flipped,\n // bit 2 = clip box enabled. Mirrors main.wgsl's flags.y.\n flags: vec4<u32>,\n extra: ExtraClip, // cutting planes beyond the first (#7545), eye-relative\n }\n @binding(2) @group(0) var<uniform> clip: Clip;\n\n struct FlatIn {\n @location(0) position: vec3<f32>,\n @location(2) entityId: u32,\n }\n\n struct QuantIn {\n @location(2) entityId: u32,\n @location(0) q: vec4<u32>, // uint16x4: lattice x, y, z, packedOct\n }\n\n struct InstanceIn {\n @location(3) m0: vec4<f32>,\n @location(4) m1: vec4<f32>,\n @location(5) m2: vec4<f32>,\n @location(6) m3: vec4<f32>,\n // Per-occurrence flags lane (bit 1 = hidden), same instance buffer the\n // colour pass reads. Locations 7 (entityId) and 8 (rgba) are in the\n // shared layout but unused by the depth pass.\n @location(9) flags: u32,\n @location(7) entityId: u32,\n // Split drawable-minus-camera delta from the slot-2 delta stream.\n @location(10) anchorHigh: vec4<f32>,\n @location(11) anchorLow: vec4<f32>,\n }\n\n struct ShadowOut {\n @builtin(position) position: vec4<f32>,\n @location(0) worldPos: vec3<f32>,\n @location(1) @interpolate(flat) entityId: u32,\n }\n\n fn emit(eyePos: vec4<f32>, id: u32) -> ShadowOut {\n var out: ShadowOut;\n out.position = light.lightViewProj * eyePos;\n out.worldPos = eyePos.xyz;\n out.entityId = id;\n return out;\n }\n\n @vertex\n fn vs_shadow_flat(input: FlatIn) -> ShadowOut {\n let linear = (draw.model * vec4<f32>(input.position, 0.0)).xyz;\n return emit(rteWorldPosition(linear, RteDrawableUniform(draw.originHigh, draw.originLow)), sectionEntityFromLane(draw.entity.x, input.entityId));\n }\n\n @vertex\n fn vs_shadow_quantized(input: QuantIn) -> ShadowOut {\n let p = draw.quantParams.xyz\n + vec3<f32>(f32(input.q.x), f32(input.q.y), f32(input.q.z)) * draw.quantParams.w;\n let linear = (draw.model * vec4<f32>(p, 0.0)).xyz;\n return emit(rteWorldPosition(linear, RteDrawableUniform(draw.originHigh, draw.originLow)), sectionEntityFromLane(draw.entity.x, input.entityId));\n }\n\n @vertex\n fn vs_shadow_instanced(input: FlatIn, inst: InstanceIn) -> ShadowOut {\n // A hidden / isolation-excluded occurrence (flags bit 1) must not cast:\n // the colour pass discards it in fs_main, so collapse it to a zero-area\n // triangle here (every vertex to one clip point) \u2014 the rasterizer drops\n // it and it writes no depth. Mirrors main.wgsl's instSelected & 2u check.\n if ((inst.flags & 2u) != 0u) {\n var out: ShadowOut;\n out.position = vec4<f32>(0.0, 0.0, 0.0, 1.0);\n out.worldPos = vec3<f32>(0.0, 0.0, 0.0);\n return out;\n }\n let instMat = mat4x4<f32>(inst.m0, inst.m1, inst.m2, inst.m3);\n // Keep the local placement from the V1 matrix and add the shared\n // CPU-packed drawable-minus-camera delta. The light projection uses\n // the same RTE frame as the colour and picker submissions.\n let linear = (instMat * vec4<f32>(input.position, 0.0)).xyz;\n return emit(rteWorldPosition(linear, RteDrawableUniform(\n inst.anchorHigh, inst.anchorLow,\n )), inst.entityId);\n }\n\n @vertex\n fn vs_shadow_textured(input: FlatIn) -> ShadowOut {\n let linear = (draw.model * vec4<f32>(input.position, 0.0)).xyz;\n return emit(rteWorldPosition(linear, RteDrawableUniform(draw.originHigh, draw.originLow)), sectionEntityFromLane(draw.entity.x, input.entityId));\n }\n\n // Clipped variant: discard before the depth write, so a clipped-away\n // occluder leaves the shadow map untouched. Kept byte-for-byte in step\n // with the fs_main section/clip-box branches.\n @fragment\n fn fs_shadow_clip(@location(0) worldPos: vec3<f32>, @location(1) @interpolate(flat) entityId: u32) {\n let excluded = sectionExcluded(entityId);\n if (!excluded && (clip.flags.x & 1u) == 1u) {\n let side = select(1.0, -1.0, (clip.flags.x & 2u) == 2u);\n let distToPlane = (dot(worldPos, clip.sectionPlane.xyz) - clip.sectionPlane.w) * side;\n if (distToPlane > 0.0) {\n discard;\n }\n }\n if (!excluded && extraClipClipped(clip.extra, worldPos)) {\n discard;\n }\n if ((clip.flags.x & 4u) != 0u) {\n if (any(worldPos < clip.clipBoxMin.xyz) || any(worldPos > clip.clipBoxMax.xyz)) {\n discard;\n }\n }\n }\n";
|
|
31
31
|
//# sourceMappingURL=shadow.wgsl.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shadow.wgsl.d.ts","sourceRoot":"","sources":["../../src/shaders/shadow.wgsl.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"shadow.wgsl.d.ts","sourceRoot":"","sources":["../../src/shaders/shadow.wgsl.ts"],"names":[],"mappings":"AAQA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,kBAAkB,w3PAyI9B,CAAC"}
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
4
|
import { relativeToEyeWgsl } from './relative-to-eye.wgsl.js';
|
|
5
|
+
import { sectionExclusionsWgsl } from '../section-exclusions.js';
|
|
6
|
+
import { extraClipWgsl } from '../extra-clip-planes.js';
|
|
5
7
|
/**
|
|
6
8
|
* Sun shadow-map depth pre-pass shaders (issue #2670, Phase 2).
|
|
7
9
|
*
|
|
@@ -33,6 +35,8 @@ import { relativeToEyeWgsl } from './relative-to-eye.wgsl.js';
|
|
|
33
35
|
*/
|
|
34
36
|
export const shadowShaderSource = `
|
|
35
37
|
${relativeToEyeWgsl}
|
|
38
|
+
${extraClipWgsl}
|
|
39
|
+
${sectionExclusionsWgsl}
|
|
36
40
|
struct Light {
|
|
37
41
|
lightViewProj: mat4x4<f32>,
|
|
38
42
|
cameraHigh: vec4<f32>,
|
|
@@ -47,6 +51,7 @@ export const shadowShaderSource = `
|
|
|
47
51
|
quantParams: vec4<f32>,
|
|
48
52
|
originHigh: vec4<f32>,
|
|
49
53
|
originLow: vec4<f32>,
|
|
54
|
+
entity: vec4<u32>,
|
|
50
55
|
}
|
|
51
56
|
@binding(1) @group(0) var<uniform> draw: Draw;
|
|
52
57
|
|
|
@@ -58,14 +63,17 @@ export const shadowShaderSource = `
|
|
|
58
63
|
// x packs: bit 0 = section enabled, bit 1 = section flipped,
|
|
59
64
|
// bit 2 = clip box enabled. Mirrors main.wgsl's flags.y.
|
|
60
65
|
flags: vec4<u32>,
|
|
66
|
+
extra: ExtraClip, // cutting planes beyond the first (#7545), eye-relative
|
|
61
67
|
}
|
|
62
68
|
@binding(2) @group(0) var<uniform> clip: Clip;
|
|
63
69
|
|
|
64
70
|
struct FlatIn {
|
|
65
71
|
@location(0) position: vec3<f32>,
|
|
72
|
+
@location(2) entityId: u32,
|
|
66
73
|
}
|
|
67
74
|
|
|
68
75
|
struct QuantIn {
|
|
76
|
+
@location(2) entityId: u32,
|
|
69
77
|
@location(0) q: vec4<u32>, // uint16x4: lattice x, y, z, packedOct
|
|
70
78
|
}
|
|
71
79
|
|
|
@@ -78,6 +86,7 @@ export const shadowShaderSource = `
|
|
|
78
86
|
// colour pass reads. Locations 7 (entityId) and 8 (rgba) are in the
|
|
79
87
|
// shared layout but unused by the depth pass.
|
|
80
88
|
@location(9) flags: u32,
|
|
89
|
+
@location(7) entityId: u32,
|
|
81
90
|
// Split drawable-minus-camera delta from the slot-2 delta stream.
|
|
82
91
|
@location(10) anchorHigh: vec4<f32>,
|
|
83
92
|
@location(11) anchorLow: vec4<f32>,
|
|
@@ -86,19 +95,21 @@ export const shadowShaderSource = `
|
|
|
86
95
|
struct ShadowOut {
|
|
87
96
|
@builtin(position) position: vec4<f32>,
|
|
88
97
|
@location(0) worldPos: vec3<f32>,
|
|
98
|
+
@location(1) @interpolate(flat) entityId: u32,
|
|
89
99
|
}
|
|
90
100
|
|
|
91
|
-
fn emit(eyePos: vec4<f32
|
|
101
|
+
fn emit(eyePos: vec4<f32>, id: u32) -> ShadowOut {
|
|
92
102
|
var out: ShadowOut;
|
|
93
103
|
out.position = light.lightViewProj * eyePos;
|
|
94
104
|
out.worldPos = eyePos.xyz;
|
|
105
|
+
out.entityId = id;
|
|
95
106
|
return out;
|
|
96
107
|
}
|
|
97
108
|
|
|
98
109
|
@vertex
|
|
99
110
|
fn vs_shadow_flat(input: FlatIn) -> ShadowOut {
|
|
100
111
|
let linear = (draw.model * vec4<f32>(input.position, 0.0)).xyz;
|
|
101
|
-
return emit(rteWorldPosition(linear, RteDrawableUniform(draw.originHigh, draw.originLow)));
|
|
112
|
+
return emit(rteWorldPosition(linear, RteDrawableUniform(draw.originHigh, draw.originLow)), sectionEntityFromLane(draw.entity.x, input.entityId));
|
|
102
113
|
}
|
|
103
114
|
|
|
104
115
|
@vertex
|
|
@@ -106,7 +117,7 @@ export const shadowShaderSource = `
|
|
|
106
117
|
let p = draw.quantParams.xyz
|
|
107
118
|
+ vec3<f32>(f32(input.q.x), f32(input.q.y), f32(input.q.z)) * draw.quantParams.w;
|
|
108
119
|
let linear = (draw.model * vec4<f32>(p, 0.0)).xyz;
|
|
109
|
-
return emit(rteWorldPosition(linear, RteDrawableUniform(draw.originHigh, draw.originLow)));
|
|
120
|
+
return emit(rteWorldPosition(linear, RteDrawableUniform(draw.originHigh, draw.originLow)), sectionEntityFromLane(draw.entity.x, input.entityId));
|
|
110
121
|
}
|
|
111
122
|
|
|
112
123
|
@vertex
|
|
@@ -128,27 +139,31 @@ export const shadowShaderSource = `
|
|
|
128
139
|
let linear = (instMat * vec4<f32>(input.position, 0.0)).xyz;
|
|
129
140
|
return emit(rteWorldPosition(linear, RteDrawableUniform(
|
|
130
141
|
inst.anchorHigh, inst.anchorLow,
|
|
131
|
-
)));
|
|
142
|
+
)), inst.entityId);
|
|
132
143
|
}
|
|
133
144
|
|
|
134
145
|
@vertex
|
|
135
146
|
fn vs_shadow_textured(input: FlatIn) -> ShadowOut {
|
|
136
147
|
let linear = (draw.model * vec4<f32>(input.position, 0.0)).xyz;
|
|
137
|
-
return emit(rteWorldPosition(linear, RteDrawableUniform(draw.originHigh, draw.originLow)));
|
|
148
|
+
return emit(rteWorldPosition(linear, RteDrawableUniform(draw.originHigh, draw.originLow)), sectionEntityFromLane(draw.entity.x, input.entityId));
|
|
138
149
|
}
|
|
139
150
|
|
|
140
151
|
// Clipped variant: discard before the depth write, so a clipped-away
|
|
141
152
|
// occluder leaves the shadow map untouched. Kept byte-for-byte in step
|
|
142
153
|
// with the fs_main section/clip-box branches.
|
|
143
154
|
@fragment
|
|
144
|
-
fn fs_shadow_clip(@location(0) worldPos: vec3<f32
|
|
145
|
-
|
|
155
|
+
fn fs_shadow_clip(@location(0) worldPos: vec3<f32>, @location(1) @interpolate(flat) entityId: u32) {
|
|
156
|
+
let excluded = sectionExcluded(entityId);
|
|
157
|
+
if (!excluded && (clip.flags.x & 1u) == 1u) {
|
|
146
158
|
let side = select(1.0, -1.0, (clip.flags.x & 2u) == 2u);
|
|
147
159
|
let distToPlane = (dot(worldPos, clip.sectionPlane.xyz) - clip.sectionPlane.w) * side;
|
|
148
160
|
if (distToPlane > 0.0) {
|
|
149
161
|
discard;
|
|
150
162
|
}
|
|
151
163
|
}
|
|
164
|
+
if (!excluded && extraClipClipped(clip.extra, worldPos)) {
|
|
165
|
+
discard;
|
|
166
|
+
}
|
|
152
167
|
if ((clip.flags.x & 4u) != 0u) {
|
|
153
168
|
if (any(worldPos < clip.clipBoxMin.xyz) || any(worldPos > clip.clipBoxMax.xyz)) {
|
|
154
169
|
discard;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shadow.wgsl.js","sourceRoot":"","sources":["../../src/shaders/shadow.wgsl.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"shadow.wgsl.js","sourceRoot":"","sources":["../../src/shaders/shadow.wgsl.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAExD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG;UACxB,iBAAiB;EACzB,aAAa;EACb,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsItB,CAAC"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export declare const SYMBOLIC_FILL_WGSL = "\nstruct Camera {\n viewProj: mat4x4<f32>,\n rteViewProj: mat4x4<f32>,\n originHigh: vec4<f32>,\n originLow: vec4<f32>,\n};\n\n@group(0) @binding(0) var<uniform> camera: Camera;\n\nstruct VsIn {\n @location(0) position: vec3<f32>,\n @location(1) color: vec4<f32>,\n};\n\nstruct VsOut {\n @builtin(position) clipPos: vec4<f32>,\n @location(0) color: vec4<f32>,\n};\n\n@vertex\nfn vs_main(in: VsIn) -> VsOut {\n var out: VsOut;\n let global = camera.viewProj * vec4<f32>(in.position, 1.0);\n let relative = (in.position + camera.originHigh.xyz) + camera.originLow.xyz;\n out.clipPos = select(global, camera.rteViewProj * vec4<f32>(relative, 1.0), camera.originHigh.w == 1.0);\n out.color = in.color;\n return out;\n}\n\n@fragment\nfn fs_main(in: VsOut) -> @location(0) vec4<f32> {\n // Premultiply alpha so the standard \"src * 1 + dst * (1-src.a)\" blend\n // produces the correct composite on top of the existing scene.\n return vec4<f32>(in.color.rgb * in.color.a, in.color.a);\n}\n";
|
|
2
|
-
export declare const SYMBOLIC_TEXT_WGSL = "\n\n \n const ORTHOGRAPHIC_DEPTH_NUDGE_PER_LEVEL: f32 = 4.76837158203125e-7;\n const ORTHOGRAPHIC_MAX_DEPTH_NUDGE_METRES: f32 = 0.025;\n const MAX_DEPTH_NUDGE_HASH: f32 = 255.0;\n\n // An orthographic view-projection leaves w at 1: its bottom row is exactly\n // (0, 0, 0, 1), because the projection's and the view's bottom rows are\n // both (0, 0, 0, 1). A perspective one copies the forward axis into that\n // row, a unit vector, so it is never zero. Every caller passes the\n // camera's world view-projection, which each draw packs whether or not\n // it renders camera-relative.\n fn isOrthographicProjection(viewProj: mat4x4<f32>) -> bool {\n return viewProj[0][3] == 0.0 && viewProj[1][3] == 0.0 && viewProj[2][3] == 0.0 && viewProj[3][3] == 1.0;\n }\n\n // Orthographic only: how many hash levels fit the metre bound. The z row\n // of the view-projection has length 1 / (far - near).\n fn orthographicNudgeLevels(viewProj: mat4x4<f32>) -> u32 {\n let depthRange = 1.0 / length(vec3<f32>(viewProj[0][2], viewProj[1][2], viewProj[2][2]));\n let steps = ORTHOGRAPHIC_MAX_DEPTH_NUDGE_METRES / (depthRange * ORTHOGRAPHIC_DEPTH_NUDGE_PER_LEVEL);\n return u32(clamp(floor(steps), 0.0, MAX_DEPTH_NUDGE_HASH)) + 1u;\n }\n\n // Orthographic only: clip z moved `ndcOffset` toward the camera without\n // changing which side of a clip plane the vertex is on. A vertex beyond\n // either plane, or exactly on the far plane, stays where it is.\n fn orthographicOffsetInsideDepthRange(clip: vec4<f32>, ndcOffset: f32) -> f32 {\n if (!(clip.z > 0.0 && clip.z < clip.w)) { return clip.z; }\n return min(clip.z + ndcOffset * clip.w, clip.w);\n }\n\n const PERSPECTIVE_OVERLAY_DEPTH_LIFT: f32 = 0.00005;\n\n // Clip-space z raised above any nudged mesh face (depth-nudge.wgsl.ts).\n // A multiple of clip.w is a constant NDC offset after the w-divide, which\n // under reverse-Z reads as \"slightly closer\". Orthographic keeps at least\n // the perspective lift, and two levels above the highest nudge level\n // when that is more (all 256 levels in use), so the text pipeline's\n // constant depthBias of -4 units is covered too.\n fn overlayLiftedClipZ(clip: vec4<f32>, viewProj: mat4x4<f32>) -> f32 {\n if (isOrthographicProjection(viewProj)) {\n let levelsLift = f32(orthographicNudgeLevels(viewProj) + 1u) * ORTHOGRAPHIC_DEPTH_NUDGE_PER_LEVEL;\n return orthographicOffsetInsideDepthRange(clip, max(PERSPECTIVE_OVERLAY_DEPTH_LIFT, levelsLift));\n }\n return clip.z + PERSPECTIVE_OVERLAY_DEPTH_LIFT * clip.w;\n }\n\nstruct Camera {\n viewProj: mat4x4<f32>,\n // Legacy labels keep using viewProj. Anchored labels select this\n // translation-free eye-relative matrix per instance.\n rteViewProj: mat4x4<f32>,\n // x = viewport width in physical pixels, y = viewport height\n // z = target glyph cap-height in screen pixels, w = padding\n viewportAndTarget: vec4<f32>,\n // Screen-aligned camera basis (world space). xyz = direction, w = padding.\n // Used by billboarded glyphs (grid tags) so they always face the camera \u2014\n // critical for top-down/ground views where world-up upAxis collapses to\n // zero screen extent and the tag becomes invisible.\n cameraRight: vec4<f32>,\n cameraUp: vec4<f32>,\n // Retained for the 208-byte symbolic-text uniform ABI. RTE text now packs\n // its drawable-minus-camera delta on the CPU rather than subtracting these\n // independently rounded absolute values in WGSL.\n cameraHigh: vec4<f32>,\n cameraLow: vec4<f32>,\n};\n\n@group(0) @binding(0) var<uniform> camera: Camera;\n@group(0) @binding(1) var atlasTex: texture_2d<f32>;\n@group(0) @binding(2) var atlasSamp: sampler;\n\n// Per-vertex (corner of the glyph quad).\nstruct VsIn {\n // Bit-packed corner index 0..3. The vertex buffer is just [0u, 1u, 2u, 3u].\n @location(0) corner: u32,\n};\n\n// Per-instance attributes \u2014 one record per glyph.\nstruct InstIn {\n // World-space origin of the glyph (bottom-left, after alignment + rotation).\n @location(1) origin: vec3<f32>,\n // Right axis in world space \u2014 encodes both glyph width and baseline rotation.\n @location(2) rightAxis: vec3<f32>,\n // Up axis in world space \u2014 encodes glyph height and baseline rotation.\n @location(3) upAxis: vec3<f32>,\n // Atlas UV bounds: (u0, v0, u1, v1).\n @location(4) uvBounds: vec4<f32>,\n // Glyph tint (sRGB straight-alpha).\n @location(5) color: vec4<f32>,\n // Shared text-label anchor (every glyph in the same label uses the same\n // anchor). Lets the shader compute one screen-space scale per label and\n // apply it uniformly to every glyph offset so row spacing stays in sync.\n @location(6) anchor: vec3<f32>,\n // Authored cap height in world units. Same value for every glyph in the\n // label; used to convert \"target pixels\" into a scale factor.\n @location(7) capHeight: f32,\n // Billboard flag (1.0 = use camera-aligned axes, 0.0 = use authored\n // rightAxis/upAxis). Only the grid-tag emission path sets this to 1.0.\n @location(8) billboard: f32,\n // Per-glyph baseline-relative offset + size in WORLD units. Only used\n // when billboard=1 (the authored origin bakes the offset along the\n // floor-plane basis, which is the wrong direction for camera-aligned\n // text). For non-billboard glyphs these are written too but the shader\n // picks the authored path. World units = atlas-pixel * wScale.\n // .xy = (offsetX, offsetY) from anchor to glyph BL\n // .zw = (width, height) of the glyph quad\n @location(9) glyphOffsetSize: vec4<f32>,\n // Per-instance target cap height in screen pixels. 0 = fall back to\n // the renderer global default (viewportAndTarget.z). Grid bubble\n // glyphs override with a larger value so the bubble stays proportional\n // to the tag at every zoom level.\n @location(10) targetPxOverride: f32,\n // CPU-packed drawable-minus-camera delta. deltaHigh.w == 1 selects the\n // RTE route; deltaLow.w marks an anchor-local static glyph origin for the\n // legacy projection path too.\n @location(11) deltaHigh: vec4<f32>,\n @location(12) deltaLow: vec4<f32>,\n};\n\nstruct VsOut {\n @builtin(position) clipPos: vec4<f32>,\n @location(0) uv: vec2<f32>,\n @location(1) color: vec4<f32>,\n};\n\nfn rteClipPosition(\n local: vec3<f32>, high: vec3<f32>, low: vec3<f32>, viewProj: mat4x4<f32>,\n) -> vec4<f32> {\n // Do not first reconstruct a million-metre f32 position: that addition can\n // discard centimetre local lanes before projection. Transform high and the\n // combined local/low term independently, then combine in clip space where projection has\n // reduced or cancelled the coordinate scale. A final raw f32 eye vector\n // still cannot represent a sub-ULP residual at the \u00B11,000,000 m boundary.\n return viewProj * vec4<f32>(high, 1.0) + viewProj * vec4<f32>(local + low, 0.0);\n}\n\n@vertex\nfn vs_main(in: VsIn, inst: InstIn) -> VsOut {\n // Map corner index to (u, v) in [0, 1] \u2014 the four quad corners.\n // 0 = bottom-left, 1 = bottom-right, 2 = top-left, 3 = top-right.\n let u = f32((in.corner & 1u)); // 0,1,0,1\n let v = f32((in.corner >> 1u) & 1u); // 0,0,1,1\n\n // \u2500\u2500 Screen-space text scaling (projection-agnostic) \u2500\u2500\n // For non-billboard glyphs measure world-Y \u2192 screen pixels (matches the\n // authored upAxis convention). For billboard glyphs measure cameraUp \u2192\n // screen pixels \u2014 that axis ALWAYS spans the full viewport height\n // regardless of view angle, so screen-space scaling stays correct in\n // top-down / ground / oblique views.\n let isBillboard = inst.billboard > 0.5;\n let isRte = inst.deltaHigh.w == 1.0;\n let hasLocalOrigin = inst.deltaLow.w == 1.0;\n // The f64 CPU path has already formed the drawable-camera delta. Project\n // split terms independently so projection can retain low residuals.\n let scaleProbeAxis = select(vec3<f32>(0.0, 1.0, 0.0), camera.cameraUp.xyz, isBillboard);\n let aClip = select(\n camera.viewProj * vec4<f32>(inst.anchor, 1.0),\n rteClipPosition(vec3<f32>(0.0), inst.deltaHigh.xyz, inst.deltaLow.xyz, camera.rteViewProj),\n isRte,\n );\n let bClip = select(\n camera.viewProj * vec4<f32>(inst.anchor + scaleProbeAxis, 1.0),\n rteClipPosition(scaleProbeAxis, inst.deltaHigh.xyz, inst.deltaLow.xyz, camera.rteViewProj),\n isRte,\n );\n let aNdc = aClip.xy / max(abs(aClip.w), 1e-4);\n let bNdc = bClip.xy / max(abs(bClip.w), 1e-4);\n let unitYPx = length(bNdc - aNdc) * camera.viewportAndTarget.y * 0.5;\n\n let safeCap = max(inst.capHeight, 1e-4);\n let currentPx = safeCap * unitYPx;\n // Per-instance target overrides the uniform default (used by grid bubble\n // glyphs to render at a larger on-screen size than the inscribed tag).\n let targetPx = select(camera.viewportAndTarget.z, inst.targetPxOverride, inst.targetPxOverride > 0.0);\n // Clamp to (0.02, 1.0]: never grow text beyond authored size; never\n // shrink below ~2%.\n let scale = clamp(\n targetPx / max(currentPx, 1e-2),\n 0.02,\n 1.0,\n );\n\n // \u2500\u2500 Position the glyph quad in world space \u2500\u2500\n // Non-billboard: authored axes (text lies in the floor plane of its\n // annotation \u2014 IFC convention). Billboard: camera-aligned axes (text\n // always faces the camera \u2014 grid-tag convention).\n // Anchored producers retain a small glyph-local origin; legacy producers\n // retain the original absolute origin and derive its local offset here.\n let authoredLocalOffset = select(inst.origin - inst.anchor, inst.origin, hasLocalOrigin);\n let authoredLocalPos =\n authoredLocalOffset * scale\n + inst.rightAxis * scale * u\n + inst.upAxis * scale * v;\n\n // Billboard: rebuild the quad in screen-aligned camera basis. glyphOffsetSize\n // carries the same 2D atlas-pixel layout the upload computed, just in\n // world units \u2014 re-project onto (cameraRight, cameraUp) so it tracks the\n // viewer's eye in every orientation.\n let bbOffsetX = inst.glyphOffsetSize.x;\n let bbOffsetY = inst.glyphOffsetSize.y;\n let bbWidth = inst.glyphOffsetSize.z;\n let bbHeight = inst.glyphOffsetSize.w;\n let billboardLocalPos =\n (camera.cameraRight.xyz * bbOffsetX + camera.cameraUp.xyz * bbOffsetY) * scale\n + (camera.cameraRight.xyz * bbWidth * u\n + camera.cameraUp.xyz * bbHeight * v) * scale;\n\n let glyphLocalPos = select(authoredLocalPos, billboardLocalPos, isBillboard);\n let worldPos = inst.anchor + glyphLocalPos;\n\n // UV: lerp atlas bounds. Note v inverted (atlas top is v=0).\n let uMix = mix(inst.uvBounds.x, inst.uvBounds.z, u);\n let vMix = mix(inst.uvBounds.w, inst.uvBounds.y, v);\n\n var out: VsOut;\n let clip = select(\n camera.viewProj * vec4<f32>(worldPos, 1.0),\n rteClipPosition(glyphLocalPos, inst.deltaHigh.xyz, inst.deltaLow.xyz, camera.rteViewProj),\n isRte,\n );\n // Reverse-Z decal nudge for text coplanar with model faces (issue #812\n // follow-up: \"30\", \"1.49\" etc. flickering against the terrain). The\n // pipeline-level depthBiasSlopeScale collapses to ~0 for billboard\n // glyphs \u2014 the quad faces the camera, so depth slope across the quad\n // is zero \u2014 leaving only a tiny -4 constant that MSAA jitter beats.\n // Lift it in clip space instead, the same way the line pipeline does,\n // above MSAA jitter and the mesh depth nudge.\n out.clipPos = vec4<f32>(clip.x, clip.y, overlayLiftedClipZ(clip, camera.viewProj), clip.w);\n out.uv = vec2<f32>(uMix, vMix);\n out.color = inst.color;\n return out;\n}\n\n// Halo (#5388): labels sit over model geometry as often as over the empty\n// backdrop, so no single ink is legible on both. Every glyph gets a thin\n// contrasting outline: the glyph coverage dilated by HALO_TEXELS, drawn under\n// the glyph in black (light text) or white (dark text). The quad and its UVs\n// were widened by the same margin on upload, and the atlas packs glyphs far\n// enough apart that the taps never reach a neighbour.\nconst HALO_TEXELS: f32 = 6.0;\nconst HALO_OPACITY: f32 = 0.8;\nconst HALO_DIRS = array<vec2<f32>, 8>(\n vec2<f32>(1.0, 0.0), vec2<f32>(-1.0, 0.0), vec2<f32>(0.0, 1.0), vec2<f32>(0.0, -1.0),\n vec2<f32>(0.7071, 0.7071), vec2<f32>(-0.7071, 0.7071), vec2<f32>(0.7071, -0.7071), vec2<f32>(-0.7071, -0.7071),\n);\n\n@fragment\nfn fs_main(in: VsOut) -> @location(0) vec4<f32> {\n // The atlas was rasterised with white glyphs on a transparent background,\n // so atlas.a is the coverage.\n let coverage = textureSample(atlasTex, atlasSamp, in.uv).a;\n let texel = 1.0 / vec2<f32>(textureDimensions(atlasTex));\n var dilated = coverage;\n // Two rings (full and half radius) so thin strokes and counters fill in.\n for (var i = 0u; i < 8u; i = i + 1u) {\n let d = HALO_DIRS[i] * texel * HALO_TEXELS;\n dilated = max(dilated, textureSample(atlasTex, atlasSamp, in.uv + d).a);\n dilated = max(dilated, textureSample(atlasTex, atlasSamp, in.uv + d * 0.5).a);\n }\n let luminance = dot(in.color.rgb, vec3<f32>(0.2126, 0.7152, 0.0722));\n let haloRgb = select(vec3<f32>(1.0), vec3<f32>(0.0), luminance > 0.5);\n // Glyph over halo, premultiplied for the standard composite.\n let glyphA = coverage * in.color.a;\n let haloA = dilated * HALO_OPACITY * in.color.a * (1.0 - glyphA);\n return vec4<f32>(in.color.rgb * glyphA + haloRgb * haloA, glyphA + haloA);\n}\n";
|
|
2
|
+
export declare const SYMBOLIC_TEXT_WGSL = "\n\n \n const ORTHOGRAPHIC_DEPTH_NUDGE_PER_LEVEL: f32 = 4.76837158203125e-7;\n const ORTHOGRAPHIC_MAX_DEPTH_NUDGE_METRES: f32 = 0.025;\n const MAX_DEPTH_NUDGE_HASH: f32 = 255.0;\n\n fn isOrthographicProjection(viewProj: mat4x4<f32>) -> bool {\n return viewProj[0][3] == 0.0 && viewProj[1][3] == 0.0 && viewProj[2][3] == 0.0 && viewProj[3][3] == 1.0;\n }\n\n fn orthographicNudgeLevels(viewProj: mat4x4<f32>) -> u32 {\n let depthRange = 1.0 / length(vec3<f32>(viewProj[0][2], viewProj[1][2], viewProj[2][2]));\n let steps = ORTHOGRAPHIC_MAX_DEPTH_NUDGE_METRES / (depthRange * ORTHOGRAPHIC_DEPTH_NUDGE_PER_LEVEL);\n return u32(clamp(floor(steps), 0.0, MAX_DEPTH_NUDGE_HASH)) + 1u;\n }\n\n fn orthographicOffsetInsideDepthRange(clip: vec4<f32>, ndcOffset: f32) -> f32 {\n if (!(clip.z > 0.0 && clip.z < clip.w)) { return clip.z; }\n return min(clip.z + ndcOffset * clip.w, clip.w);\n }\n\n const PERSPECTIVE_OVERLAY_DEPTH_LIFT: f32 = 0.00005;\n\n fn overlayLiftedClipZ(clip: vec4<f32>, viewProj: mat4x4<f32>) -> f32 {\n if (isOrthographicProjection(viewProj)) {\n let levelsLift = f32(orthographicNudgeLevels(viewProj) + 1u) * ORTHOGRAPHIC_DEPTH_NUDGE_PER_LEVEL;\n return orthographicOffsetInsideDepthRange(clip, max(PERSPECTIVE_OVERLAY_DEPTH_LIFT, levelsLift));\n }\n return clip.z + PERSPECTIVE_OVERLAY_DEPTH_LIFT * clip.w;\n }\n\nstruct Camera {\n viewProj: mat4x4<f32>,\n // Legacy labels keep using viewProj. Anchored labels select this\n // translation-free eye-relative matrix per instance.\n rteViewProj: mat4x4<f32>,\n // x = viewport width in physical pixels, y = viewport height\n // z = target glyph cap-height in screen pixels, w = padding\n viewportAndTarget: vec4<f32>,\n // Screen-aligned camera basis (world space). xyz = direction, w = padding.\n // Used by billboarded glyphs (grid tags) so they always face the camera \u2014\n // critical for top-down/ground views where world-up upAxis collapses to\n // zero screen extent and the tag becomes invisible.\n cameraRight: vec4<f32>,\n cameraUp: vec4<f32>,\n // Retained for the 208-byte symbolic-text uniform ABI. RTE text now packs\n // its drawable-minus-camera delta on the CPU rather than subtracting these\n // independently rounded absolute values in WGSL.\n cameraHigh: vec4<f32>,\n cameraLow: vec4<f32>,\n};\n\n@group(0) @binding(0) var<uniform> camera: Camera;\n@group(0) @binding(1) var atlasTex: texture_2d<f32>;\n@group(0) @binding(2) var atlasSamp: sampler;\n\n// Per-vertex (corner of the glyph quad).\nstruct VsIn {\n // Bit-packed corner index 0..3. The vertex buffer is just [0u, 1u, 2u, 3u].\n @location(0) corner: u32,\n};\n\n// Per-instance attributes \u2014 one record per glyph.\nstruct InstIn {\n // World-space origin of the glyph (bottom-left, after alignment + rotation).\n @location(1) origin: vec3<f32>,\n // Right axis in world space \u2014 encodes both glyph width and baseline rotation.\n @location(2) rightAxis: vec3<f32>,\n // Up axis in world space \u2014 encodes glyph height and baseline rotation.\n @location(3) upAxis: vec3<f32>,\n // Atlas UV bounds: (u0, v0, u1, v1).\n @location(4) uvBounds: vec4<f32>,\n // Glyph tint (sRGB straight-alpha).\n @location(5) color: vec4<f32>,\n // Shared text-label anchor (every glyph in the same label uses the same\n // anchor). Lets the shader compute one screen-space scale per label and\n // apply it uniformly to every glyph offset so row spacing stays in sync.\n @location(6) anchor: vec3<f32>,\n // Authored cap height in world units. Same value for every glyph in the\n // label; used to convert \"target pixels\" into a scale factor.\n @location(7) capHeight: f32,\n // Billboard flag (1.0 = use camera-aligned axes, 0.0 = use authored\n // rightAxis/upAxis). Only the grid-tag emission path sets this to 1.0.\n @location(8) billboard: f32,\n // Per-glyph baseline-relative offset + size in WORLD units. Only used\n // when billboard=1 (the authored origin bakes the offset along the\n // floor-plane basis, which is the wrong direction for camera-aligned\n // text). For non-billboard glyphs these are written too but the shader\n // picks the authored path. World units = atlas-pixel * wScale.\n // .xy = (offsetX, offsetY) from anchor to glyph BL\n // .zw = (width, height) of the glyph quad\n @location(9) glyphOffsetSize: vec4<f32>,\n // Per-instance target cap height in screen pixels. 0 = fall back to\n // the renderer global default (viewportAndTarget.z). Grid bubble\n // glyphs override with a larger value so the bubble stays proportional\n // to the tag at every zoom level.\n @location(10) targetPxOverride: f32,\n // CPU-packed drawable-minus-camera delta. deltaHigh.w == 1 selects the\n // RTE route; deltaLow.w marks an anchor-local static glyph origin for the\n // legacy projection path too.\n @location(11) deltaHigh: vec4<f32>,\n @location(12) deltaLow: vec4<f32>,\n};\n\nstruct VsOut {\n @builtin(position) clipPos: vec4<f32>,\n @location(0) uv: vec2<f32>,\n @location(1) color: vec4<f32>,\n};\n\nfn rteClipPosition(\n local: vec3<f32>, high: vec3<f32>, low: vec3<f32>, viewProj: mat4x4<f32>,\n) -> vec4<f32> {\n // Do not first reconstruct a million-metre f32 position: that addition can\n // discard centimetre local lanes before projection. Transform high and the\n // combined local/low term independently, then combine in clip space where projection has\n // reduced or cancelled the coordinate scale. A final raw f32 eye vector\n // still cannot represent a sub-ULP residual at the \u00B11,000,000 m boundary.\n return viewProj * vec4<f32>(high, 1.0) + viewProj * vec4<f32>(local + low, 0.0);\n}\n\n@vertex\nfn vs_main(in: VsIn, inst: InstIn) -> VsOut {\n // Map corner index to (u, v) in [0, 1] \u2014 the four quad corners.\n // 0 = bottom-left, 1 = bottom-right, 2 = top-left, 3 = top-right.\n let u = f32((in.corner & 1u)); // 0,1,0,1\n let v = f32((in.corner >> 1u) & 1u); // 0,0,1,1\n\n // \u2500\u2500 Screen-space text scaling (projection-agnostic) \u2500\u2500\n // For non-billboard glyphs measure world-Y \u2192 screen pixels (matches the\n // authored upAxis convention). For billboard glyphs measure cameraUp \u2192\n // screen pixels \u2014 that axis ALWAYS spans the full viewport height\n // regardless of view angle, so screen-space scaling stays correct in\n // top-down / ground / oblique views.\n let isBillboard = inst.billboard > 0.5;\n let isRte = inst.deltaHigh.w == 1.0;\n let hasLocalOrigin = inst.deltaLow.w == 1.0;\n // The f64 CPU path has already formed the drawable-camera delta. Project\n // split terms independently so projection can retain low residuals.\n let scaleProbeAxis = select(vec3<f32>(0.0, 1.0, 0.0), camera.cameraUp.xyz, isBillboard);\n let aClip = select(\n camera.viewProj * vec4<f32>(inst.anchor, 1.0),\n rteClipPosition(vec3<f32>(0.0), inst.deltaHigh.xyz, inst.deltaLow.xyz, camera.rteViewProj),\n isRte,\n );\n let bClip = select(\n camera.viewProj * vec4<f32>(inst.anchor + scaleProbeAxis, 1.0),\n rteClipPosition(scaleProbeAxis, inst.deltaHigh.xyz, inst.deltaLow.xyz, camera.rteViewProj),\n isRte,\n );\n let aNdc = aClip.xy / max(abs(aClip.w), 1e-4);\n let bNdc = bClip.xy / max(abs(bClip.w), 1e-4);\n let unitYPx = length(bNdc - aNdc) * camera.viewportAndTarget.y * 0.5;\n\n let safeCap = max(inst.capHeight, 1e-4);\n let currentPx = safeCap * unitYPx;\n // Per-instance target overrides the uniform default (used by grid bubble\n // glyphs to render at a larger on-screen size than the inscribed tag).\n let targetPx = select(camera.viewportAndTarget.z, inst.targetPxOverride, inst.targetPxOverride > 0.0);\n // Clamp to (0.02, 1.0]: never grow text beyond authored size; never\n // shrink below ~2%.\n let scale = clamp(\n targetPx / max(currentPx, 1e-2),\n 0.02,\n 1.0,\n );\n\n // \u2500\u2500 Position the glyph quad in world space \u2500\u2500\n // Non-billboard: authored axes (text lies in the floor plane of its\n // annotation \u2014 IFC convention). Billboard: camera-aligned axes (text\n // always faces the camera \u2014 grid-tag convention).\n // Anchored producers retain a small glyph-local origin; legacy producers\n // retain the original absolute origin and derive its local offset here.\n let authoredLocalOffset = select(inst.origin - inst.anchor, inst.origin, hasLocalOrigin);\n let authoredLocalPos =\n authoredLocalOffset * scale\n + inst.rightAxis * scale * u\n + inst.upAxis * scale * v;\n\n // Billboard: rebuild the quad in screen-aligned camera basis. glyphOffsetSize\n // carries the same 2D atlas-pixel layout the upload computed, just in\n // world units \u2014 re-project onto (cameraRight, cameraUp) so it tracks the\n // viewer's eye in every orientation.\n let bbOffsetX = inst.glyphOffsetSize.x;\n let bbOffsetY = inst.glyphOffsetSize.y;\n let bbWidth = inst.glyphOffsetSize.z;\n let bbHeight = inst.glyphOffsetSize.w;\n let billboardLocalPos =\n (camera.cameraRight.xyz * bbOffsetX + camera.cameraUp.xyz * bbOffsetY) * scale\n + (camera.cameraRight.xyz * bbWidth * u\n + camera.cameraUp.xyz * bbHeight * v) * scale;\n\n let glyphLocalPos = select(authoredLocalPos, billboardLocalPos, isBillboard);\n let worldPos = inst.anchor + glyphLocalPos;\n\n // UV: lerp atlas bounds. Note v inverted (atlas top is v=0).\n let uMix = mix(inst.uvBounds.x, inst.uvBounds.z, u);\n let vMix = mix(inst.uvBounds.w, inst.uvBounds.y, v);\n\n var out: VsOut;\n let clip = select(\n camera.viewProj * vec4<f32>(worldPos, 1.0),\n rteClipPosition(glyphLocalPos, inst.deltaHigh.xyz, inst.deltaLow.xyz, camera.rteViewProj),\n isRte,\n );\n // Reverse-Z decal nudge for text coplanar with model faces (issue #812\n // follow-up: \"30\", \"1.49\" etc. flickering against the terrain). The\n // pipeline-level depthBiasSlopeScale collapses to ~0 for billboard\n // glyphs \u2014 the quad faces the camera, so depth slope across the quad\n // is zero \u2014 leaving only a tiny -4 constant that MSAA jitter beats.\n // Lift it in clip space instead, the same way the line pipeline does,\n // above MSAA jitter and the mesh depth nudge.\n out.clipPos = vec4<f32>(clip.x, clip.y, overlayLiftedClipZ(clip, camera.viewProj), clip.w);\n out.uv = vec2<f32>(uMix, vMix);\n out.color = inst.color;\n return out;\n}\n\n// Halo (#5388): labels sit over model geometry as often as over the empty\n// backdrop, so no single ink is legible on both. Every glyph gets a thin\n// contrasting outline: the glyph coverage dilated by HALO_TEXELS, drawn under\n// the glyph in black (light text) or white (dark text). The quad and its UVs\n// were widened by the same margin on upload, and the atlas packs glyphs far\n// enough apart that the taps never reach a neighbour.\nconst HALO_TEXELS: f32 = 6.0;\nconst HALO_OPACITY: f32 = 0.8;\nconst HALO_DIRS = array<vec2<f32>, 8>(\n vec2<f32>(1.0, 0.0), vec2<f32>(-1.0, 0.0), vec2<f32>(0.0, 1.0), vec2<f32>(0.0, -1.0),\n vec2<f32>(0.7071, 0.7071), vec2<f32>(-0.7071, 0.7071), vec2<f32>(0.7071, -0.7071), vec2<f32>(-0.7071, -0.7071),\n);\n\n@fragment\nfn fs_main(in: VsOut) -> @location(0) vec4<f32> {\n // The atlas was rasterised with white glyphs on a transparent background,\n // so atlas.a is the coverage.\n let coverage = textureSample(atlasTex, atlasSamp, in.uv).a;\n let texel = 1.0 / vec2<f32>(textureDimensions(atlasTex));\n var dilated = coverage;\n // Two rings (full and half radius) so thin strokes and counters fill in.\n for (var i = 0u; i < 8u; i = i + 1u) {\n let d = HALO_DIRS[i] * texel * HALO_TEXELS;\n dilated = max(dilated, textureSample(atlasTex, atlasSamp, in.uv + d).a);\n dilated = max(dilated, textureSample(atlasTex, atlasSamp, in.uv + d * 0.5).a);\n }\n let luminance = dot(in.color.rgb, vec3<f32>(0.2126, 0.7152, 0.0722));\n let haloRgb = select(vec3<f32>(1.0), vec3<f32>(0.0), luminance > 0.5);\n // Glyph over halo, premultiplied for the standard composite.\n let glyphA = coverage * in.color.a;\n let haloA = dilated * HALO_OPACITY * in.color.a * (1.0 - glyphA);\n return vec4<f32>(in.color.rgb * glyphA + haloRgb * haloA, glyphA + haloA);\n}\n";
|
|
3
3
|
//# sourceMappingURL=symbolic-overlay.wgsl.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"symbolic-overlay.wgsl.d.ts","sourceRoot":"","sources":["../../src/shaders/symbolic-overlay.wgsl.ts"],"names":[],"mappings":"AAeA,eAAO,MAAM,kBAAkB,0/BAoC9B,CAAC;AAEF,eAAO,MAAM,kBAAkB,
|
|
1
|
+
{"version":3,"file":"symbolic-overlay.wgsl.d.ts","sourceRoot":"","sources":["../../src/shaders/symbolic-overlay.wgsl.ts"],"names":[],"mappings":"AAeA,eAAO,MAAM,kBAAkB,0/BAoC9B,CAAC;AAEF,eAAO,MAAM,kBAAkB,inYA8N9B,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shadow-occluders.d.ts","sourceRoot":"","sources":["../src/shadow-occluders.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"shadow-occluders.d.ts","sourceRoot":"","sources":["../src/shadow-occluders.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,KAAK,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACrE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAG3D,qEAAqE;AACrE,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,SAAS,WAAW,EAAE,CAAC;IAChC,SAAS,EAAE,SAAS,oBAAoB,EAAE,CAAC;IAC3C,QAAQ,EAAE,SAAS,YAAY,EAAE,CAAC;IAClC;;;;;OAKG;IACH,MAAM,CAAC,EAAE,SAAS,IAAI,EAAE,CAAC;CAC1B;AAED,oDAAoD;AACpD,MAAM,WAAW,gBAAgB;IAC/B,SAAS,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAChC,WAAW,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CACnC;AAED,iDAAiD;AACjD,MAAM,WAAW,qBAAqB;IACpC;;;;;;;;;OASG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,EACrD,GAAG,GAAE,YAAmC,GACvC,YAAY,CASd;AAsBD;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,oBAAoB,GAC5B;IAAE,IAAI,EAAE,KAAK,CAAA;CAAE,GACf;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CAAE,CAAC;AAEjD,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,SAAS,MAAM,EAAE,EAC7B,SAAS,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,IAAI,GAAG,SAAS,EACjD,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,IAAI,GAAG,SAAS,GAClD,oBAAoB,CAYtB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,qBAAqB,EAC9B,UAAU,CAAC,EAAE,gBAAgB,EAC7B,OAAO,CAAC,EAAE,qBAAqB,GAC9B,kBAAkB,EAAE,CAwEtB"}
|