@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.
Files changed (284) hide show
  1. package/dist/camera-projection.d.ts +2 -0
  2. package/dist/camera-projection.d.ts.map +1 -1
  3. package/dist/camera-projection.js +46 -0
  4. package/dist/camera-projection.js.map +1 -1
  5. package/dist/chunk-grid.d.ts +21 -0
  6. package/dist/chunk-grid.d.ts.map +1 -1
  7. package/dist/chunk-grid.js +25 -7
  8. package/dist/chunk-grid.js.map +1 -1
  9. package/dist/clash-solid-pipeline.d.ts.map +1 -1
  10. package/dist/clash-solid-pipeline.js +4 -2
  11. package/dist/clash-solid-pipeline.js.map +1 -1
  12. package/dist/deviation/deviation-computer.d.ts +4 -0
  13. package/dist/deviation/deviation-computer.d.ts.map +1 -1
  14. package/dist/deviation/deviation-computer.js +20 -0
  15. package/dist/deviation/deviation-computer.js.map +1 -1
  16. package/dist/deviation/deviation-readback.d.ts +18 -0
  17. package/dist/deviation/deviation-readback.d.ts.map +1 -0
  18. package/dist/deviation/deviation-readback.js +67 -0
  19. package/dist/deviation/deviation-readback.js.map +1 -0
  20. package/dist/edge-pass.d.ts +16 -0
  21. package/dist/edge-pass.d.ts.map +1 -1
  22. package/dist/edge-pass.js +75 -1
  23. package/dist/edge-pass.js.map +1 -1
  24. package/dist/entity-color-table.d.ts +140 -0
  25. package/dist/entity-color-table.d.ts.map +1 -0
  26. package/dist/entity-color-table.js +238 -0
  27. package/dist/entity-color-table.js.map +1 -0
  28. package/dist/gpu-upload-guard.d.ts +1 -1
  29. package/dist/gpu-upload-guard.js +1 -1
  30. package/dist/hover-mesh-cache.d.ts +29 -0
  31. package/dist/hover-mesh-cache.d.ts.map +1 -0
  32. package/dist/hover-mesh-cache.js +31 -0
  33. package/dist/hover-mesh-cache.js.map +1 -0
  34. package/dist/index.d.ts +14 -0
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +146 -161
  37. package/dist/index.js.map +1 -1
  38. package/dist/individual-mesh-upload.d.ts +24 -0
  39. package/dist/individual-mesh-upload.d.ts.map +1 -0
  40. package/dist/individual-mesh-upload.js +85 -0
  41. package/dist/individual-mesh-upload.js.map +1 -0
  42. package/dist/instanced-render.d.ts +19 -9
  43. package/dist/instanced-render.d.ts.map +1 -1
  44. package/dist/instanced-render.js +36 -24
  45. package/dist/instanced-render.js.map +1 -1
  46. package/dist/instanced-rte.d.ts +68 -7
  47. package/dist/instanced-rte.d.ts.map +1 -1
  48. package/dist/instanced-rte.js +98 -17
  49. package/dist/instanced-rte.js.map +1 -1
  50. package/dist/instanced-vertex-layout.d.ts +26 -0
  51. package/dist/instanced-vertex-layout.d.ts.map +1 -0
  52. package/dist/instanced-vertex-layout.js +61 -0
  53. package/dist/instanced-vertex-layout.js.map +1 -0
  54. package/dist/mesh-rte-uniforms.d.ts +4 -2
  55. package/dist/mesh-rte-uniforms.d.ts.map +1 -1
  56. package/dist/mesh-rte-uniforms.js +4 -2
  57. package/dist/mesh-rte-uniforms.js.map +1 -1
  58. package/dist/model-translation.d.ts.map +1 -1
  59. package/dist/model-translation.js +0 -7
  60. package/dist/model-translation.js.map +1 -1
  61. package/dist/outline-params.d.ts +45 -0
  62. package/dist/outline-params.d.ts.map +1 -0
  63. package/dist/outline-params.js +54 -0
  64. package/dist/outline-params.js.map +1 -0
  65. package/dist/overlay-routing.d.ts +8 -8
  66. package/dist/overlay-routing.js +2 -2
  67. package/dist/overlay-routing.js.map +1 -1
  68. package/dist/overlay-theme.d.ts +9 -3
  69. package/dist/overlay-theme.d.ts.map +1 -1
  70. package/dist/overlay-theme.js.map +1 -1
  71. package/dist/partial-batch-requests.d.ts +2 -2
  72. package/dist/pick-mesh-budget.d.ts +28 -0
  73. package/dist/pick-mesh-budget.d.ts.map +1 -0
  74. package/dist/pick-mesh-budget.js +74 -0
  75. package/dist/pick-mesh-budget.js.map +1 -0
  76. package/dist/picker-rte-uniforms.d.ts +7 -1
  77. package/dist/picker-rte-uniforms.d.ts.map +1 -1
  78. package/dist/picker-rte-uniforms.js +9 -1
  79. package/dist/picker-rte-uniforms.js.map +1 -1
  80. package/dist/picker.d.ts.map +1 -1
  81. package/dist/picker.js +15 -28
  82. package/dist/picker.js.map +1 -1
  83. package/dist/picking-manager.d.ts.map +1 -1
  84. package/dist/picking-manager.js +4 -47
  85. package/dist/picking-manager.js.map +1 -1
  86. package/dist/pipeline.d.ts +7 -1
  87. package/dist/pipeline.d.ts.map +1 -1
  88. package/dist/pipeline.js +37 -59
  89. package/dist/pipeline.js.map +1 -1
  90. package/dist/point-picker.d.ts.map +1 -1
  91. package/dist/point-picker.js +7 -2
  92. package/dist/point-picker.js.map +1 -1
  93. package/dist/pointcloud/point-cloud-node.js +1 -1
  94. package/dist/pointcloud/point-cloud-node.js.map +1 -1
  95. package/dist/pointcloud/point-cloud-renderer.d.ts.map +1 -1
  96. package/dist/pointcloud/point-cloud-renderer.js +3 -1
  97. package/dist/pointcloud/point-cloud-renderer.js.map +1 -1
  98. package/dist/pointcloud/point-cloud-uniforms.d.ts +5 -1
  99. package/dist/pointcloud/point-cloud-uniforms.d.ts.map +1 -1
  100. package/dist/pointcloud/point-cloud-uniforms.js +7 -1
  101. package/dist/pointcloud/point-cloud-uniforms.js.map +1 -1
  102. package/dist/post-pass-chain.d.ts +14 -1
  103. package/dist/post-pass-chain.d.ts.map +1 -1
  104. package/dist/post-pass-chain.js +31 -2
  105. package/dist/post-pass-chain.js.map +1 -1
  106. package/dist/raycast-engine.d.ts +5 -1
  107. package/dist/raycast-engine.d.ts.map +1 -1
  108. package/dist/raycast-engine.js +20 -8
  109. package/dist/raycast-engine.js.map +1 -1
  110. package/dist/raycast-point-cloud-query.d.ts +2 -0
  111. package/dist/raycast-point-cloud-query.d.ts.map +1 -1
  112. package/dist/raycast-point-cloud-query.js +7 -0
  113. package/dist/raycast-point-cloud-query.js.map +1 -1
  114. package/dist/raycast-source-curve-query.d.ts +13 -0
  115. package/dist/raycast-source-curve-query.d.ts.map +1 -0
  116. package/dist/raycast-source-curve-query.js +17 -0
  117. package/dist/raycast-source-curve-query.js.map +1 -0
  118. package/dist/reference-image-pipeline.d.ts.map +1 -1
  119. package/dist/reference-image-pipeline.js +4 -2
  120. package/dist/reference-image-pipeline.js.map +1 -1
  121. package/dist/relative-to-eye-abi.d.ts +36 -0
  122. package/dist/relative-to-eye-abi.d.ts.map +1 -0
  123. package/dist/relative-to-eye-abi.js +72 -0
  124. package/dist/relative-to-eye-abi.js.map +1 -0
  125. package/dist/relative-to-eye.d.ts +15 -35
  126. package/dist/relative-to-eye.d.ts.map +1 -1
  127. package/dist/relative-to-eye.js +46 -74
  128. package/dist/relative-to-eye.js.map +1 -1
  129. package/dist/render-stats.d.ts +5 -1
  130. package/dist/render-stats.d.ts.map +1 -1
  131. package/dist/render-stats.js +1 -1
  132. package/dist/render-stats.js.map +1 -1
  133. package/dist/renderer-overlays.d.ts.map +1 -1
  134. package/dist/renderer-overlays.js +3 -2
  135. package/dist/renderer-overlays.js.map +1 -1
  136. package/dist/scene-batch-rebuild.d.ts +1 -2
  137. package/dist/scene-batch-rebuild.d.ts.map +1 -1
  138. package/dist/scene-batch-rebuild.js +4 -8
  139. package/dist/scene-batch-rebuild.js.map +1 -1
  140. package/dist/scene-batch-upload.d.ts.map +1 -1
  141. package/dist/scene-batch-upload.js +7 -3
  142. package/dist/scene-batch-upload.js.map +1 -1
  143. package/dist/scene-contents.d.ts +1 -1
  144. package/dist/scene-contents.d.ts.map +1 -1
  145. package/dist/scene-derived-batches.d.ts +19 -64
  146. package/dist/scene-derived-batches.d.ts.map +1 -1
  147. package/dist/scene-derived-batches.js +16 -51
  148. package/dist/scene-derived-batches.js.map +1 -1
  149. package/dist/scene-device-recovery.d.ts +3 -3
  150. package/dist/scene-device-recovery.d.ts.map +1 -1
  151. package/dist/scene-device-recovery.js +12 -9
  152. package/dist/scene-device-recovery.js.map +1 -1
  153. package/dist/scene-geometry.d.ts +6 -6
  154. package/dist/scene-geometry.d.ts.map +1 -1
  155. package/dist/scene-geometry.js +8 -9
  156. package/dist/scene-geometry.js.map +1 -1
  157. package/dist/scene-instance-bounds.d.ts +1 -1
  158. package/dist/scene-instance-bounds.d.ts.map +1 -1
  159. package/dist/scene-instance-bounds.js +4 -16
  160. package/dist/scene-instance-bounds.js.map +1 -1
  161. package/dist/scene-instance-materialization.d.ts.map +1 -1
  162. package/dist/scene-instance-materialization.js +4 -4
  163. package/dist/scene-instance-materialization.js.map +1 -1
  164. package/dist/scene-instance-translation.d.ts +6 -10
  165. package/dist/scene-instance-translation.d.ts.map +1 -1
  166. package/dist/scene-instance-translation.js +9 -31
  167. package/dist/scene-instance-translation.js.map +1 -1
  168. package/dist/scene-instance-types.d.ts +11 -2
  169. package/dist/scene-instance-types.d.ts.map +1 -1
  170. package/dist/scene-instance-types.js +7 -1
  171. package/dist/scene-instance-types.js.map +1 -1
  172. package/dist/scene-model-translation.d.ts +1 -2
  173. package/dist/scene-model-translation.d.ts.map +1 -1
  174. package/dist/scene-model-translation.js +4 -3
  175. package/dist/scene-model-translation.js.map +1 -1
  176. package/dist/scene-precision.d.ts +1 -1
  177. package/dist/scene-precision.d.ts.map +1 -1
  178. package/dist/scene-precision.js +11 -2
  179. package/dist/scene-precision.js.map +1 -1
  180. package/dist/scene.d.ts +43 -38
  181. package/dist/scene.d.ts.map +1 -1
  182. package/dist/scene.js +121 -129
  183. package/dist/scene.js.map +1 -1
  184. package/dist/section-2d-line-buffer.d.ts +2 -2
  185. package/dist/section-2d-line-buffer.d.ts.map +1 -1
  186. package/dist/section-2d-line-buffer.js +10 -11
  187. package/dist/section-2d-line-buffer.js.map +1 -1
  188. package/dist/section-2d-overlay.d.ts +6 -4
  189. package/dist/section-2d-overlay.d.ts.map +1 -1
  190. package/dist/section-2d-overlay.js +45 -21
  191. package/dist/section-2d-overlay.js.map +1 -1
  192. package/dist/section-2d-types.d.ts +1 -1
  193. package/dist/section-2d-types.d.ts.map +1 -1
  194. package/dist/section-2d-types.js +1 -1
  195. package/dist/section-2d-types.js.map +1 -1
  196. package/dist/section-plane.d.ts.map +1 -1
  197. package/dist/section-plane.js +3 -1
  198. package/dist/section-plane.js.map +1 -1
  199. package/dist/selection-mask-pass.d.ts +85 -0
  200. package/dist/selection-mask-pass.d.ts.map +1 -0
  201. package/dist/selection-mask-pass.js +170 -0
  202. package/dist/selection-mask-pass.js.map +1 -0
  203. package/dist/selection-mask-pipelines.d.ts +57 -0
  204. package/dist/selection-mask-pipelines.d.ts.map +1 -0
  205. package/dist/selection-mask-pipelines.js +150 -0
  206. package/dist/selection-mask-pipelines.js.map +1 -0
  207. package/dist/selection-outline-frame.d.ts +47 -0
  208. package/dist/selection-outline-frame.d.ts.map +1 -0
  209. package/dist/selection-outline-frame.js +122 -0
  210. package/dist/selection-outline-frame.js.map +1 -0
  211. package/dist/shaders/depth-reconstruct.wgsl.d.ts +7 -2
  212. package/dist/shaders/depth-reconstruct.wgsl.d.ts.map +1 -1
  213. package/dist/shaders/depth-reconstruct.wgsl.js +8 -3
  214. package/dist/shaders/depth-reconstruct.wgsl.js.map +1 -1
  215. package/dist/shaders/edges.wgsl.d.ts +18 -0
  216. package/dist/shaders/edges.wgsl.d.ts.map +1 -1
  217. package/dist/shaders/edges.wgsl.js +99 -0
  218. package/dist/shaders/edges.wgsl.js.map +1 -1
  219. package/dist/shaders/entity-color-table.wgsl.d.ts +2 -0
  220. package/dist/shaders/entity-color-table.wgsl.d.ts.map +1 -0
  221. package/dist/shaders/entity-color-table.wgsl.js +85 -0
  222. package/dist/shaders/entity-color-table.wgsl.js.map +1 -0
  223. package/dist/shaders/main.wgsl.d.ts +1 -1
  224. package/dist/shaders/main.wgsl.d.ts.map +1 -1
  225. package/dist/shaders/main.wgsl.js +45 -113
  226. package/dist/shaders/main.wgsl.js.map +1 -1
  227. package/dist/shaders/mesh-uniforms.wgsl.d.ts +2 -0
  228. package/dist/shaders/mesh-uniforms.wgsl.d.ts.map +1 -0
  229. package/dist/shaders/mesh-uniforms.wgsl.js +53 -0
  230. package/dist/shaders/mesh-uniforms.wgsl.js.map +1 -0
  231. package/dist/shaders/section-2d-overlay.wgsl.d.ts +1 -0
  232. package/dist/shaders/section-2d-overlay.wgsl.d.ts.map +1 -1
  233. package/dist/shaders/section-2d-overlay.wgsl.js +3 -2
  234. package/dist/shaders/section-2d-overlay.wgsl.js.map +1 -1
  235. package/dist/shaders/selection-mask.wgsl.d.ts +16 -0
  236. package/dist/shaders/selection-mask.wgsl.d.ts.map +1 -0
  237. package/dist/shaders/selection-mask.wgsl.js +138 -0
  238. package/dist/shaders/selection-mask.wgsl.js.map +1 -0
  239. package/dist/shaders/shadow.wgsl.d.ts +1 -1
  240. package/dist/shaders/shadow.wgsl.d.ts.map +1 -1
  241. package/dist/shaders/shadow.wgsl.js +3 -3
  242. package/dist/shadow-occluders.d.ts.map +1 -1
  243. package/dist/shadow-occluders.js +1 -0
  244. package/dist/shadow-occluders.js.map +1 -1
  245. package/dist/shadow-pass.d.ts +0 -1
  246. package/dist/shadow-pass.d.ts.map +1 -1
  247. package/dist/shadow-pass.js +26 -32
  248. package/dist/shadow-pass.js.map +1 -1
  249. package/dist/shadow-types.d.ts +3 -0
  250. package/dist/shadow-types.d.ts.map +1 -1
  251. package/dist/shadow-types.js.map +1 -1
  252. package/dist/snap-detector.d.ts +5 -0
  253. package/dist/snap-detector.d.ts.map +1 -1
  254. package/dist/snap-detector.js.map +1 -1
  255. package/dist/source-curve-clipping.d.ts +7 -0
  256. package/dist/source-curve-clipping.d.ts.map +1 -0
  257. package/dist/source-curve-clipping.js +165 -0
  258. package/dist/source-curve-clipping.js.map +1 -0
  259. package/dist/source-curve-snap.d.ts +49 -0
  260. package/dist/source-curve-snap.d.ts.map +1 -0
  261. package/dist/source-curve-snap.js +197 -0
  262. package/dist/source-curve-snap.js.map +1 -0
  263. package/dist/symbolic-overlay-pipelines.d.ts +1 -1
  264. package/dist/symbolic-overlay-pipelines.d.ts.map +1 -1
  265. package/dist/symbolic-overlay-pipelines.js +13 -22
  266. package/dist/symbolic-overlay-pipelines.js.map +1 -1
  267. package/dist/symbolic-text-rte.d.ts +16 -0
  268. package/dist/symbolic-text-rte.d.ts.map +1 -0
  269. package/dist/symbolic-text-rte.js +37 -0
  270. package/dist/symbolic-text-rte.js.map +1 -0
  271. package/dist/textured-vertices.d.ts.map +1 -1
  272. package/dist/textured-vertices.js +2 -3
  273. package/dist/textured-vertices.js.map +1 -1
  274. package/dist/types.d.ts +10 -0
  275. package/dist/types.d.ts.map +1 -1
  276. package/dist/visual-enhancement.d.ts +0 -4
  277. package/dist/visual-enhancement.d.ts.map +1 -1
  278. package/dist/visual-enhancement.js +0 -5
  279. package/dist/visual-enhancement.js.map +1 -1
  280. package/dist/xray-alpha.d.ts +7 -3
  281. package/dist/xray-alpha.d.ts.map +1 -1
  282. package/dist/xray-alpha.js +11 -7
  283. package/dist/xray-alpha.js.map +1 -1
  284. package/package.json +4 -4
@@ -5,14 +5,15 @@
5
5
  * Main rendering shader for IFC geometry.
6
6
  * Features: linear-space lighting of sRGB-authored colours with a GGX
7
7
  * specular term (specular.wgsl.ts), section plane clipping, selection
8
- * highlight, glass, hue-preserving highlight roll-off, screen-space edge
9
- * enhancement.
8
+ * highlight, glass, hue-preserving highlight roll-off. Edges come from the
9
+ * screen-space edge pass (edge-pass.ts), not from this shader (#5746).
10
10
  */
11
- import { MESH_FLAG_RTE_DRAWABLE } from '../mesh-rte-uniforms.js';
12
11
  import { colorTransferWgsl } from './color-transfer.wgsl.js';
13
12
  import { mainRteWgsl, mainShadowWgsl } from './main-rte-shadow.wgsl.js';
13
+ import { meshUniformsWgsl } from './mesh-uniforms.wgsl.js';
14
14
  import { relativeToEyeWgsl } from './relative-to-eye.wgsl.js';
15
15
  import { specularWgsl } from './specular.wgsl.js';
16
+ import { entityColorTableWgsl } from './entity-color-table.wgsl.js';
16
17
  /**
17
18
  * Translucent surfaces draw at this fraction of their alpha, so interiors
18
19
  * read through windows and X-Ray ghosts stay faint. A viewer choice that
@@ -37,25 +38,7 @@ const TRANSLUCENT_OPACITY_SCALE = 0.7;
37
38
  */
38
39
  const IRRADIANCE_CALIBRATION = 1.82;
39
40
  export const mainShaderSource = `
40
- struct Uniforms {
41
- viewProj: mat4x4<f32>,
42
- model: mat4x4<f32>,
43
- baseColor: vec4<f32>,
44
- metallicRoughness: vec2<f32>, // x = metallic, y = roughness (mesh-material.ts)
45
- transmission: vec2<f32>, // x = 1: authored translucent, drawn as glass; y = pad
46
- sectionPlane: vec4<f32>, // xyz = plane normal, w = plane distance
47
- flags: vec4<u32>, // x = isSelected, y = section/clip bits, z = edgeEnabled, w = edgeIntensityMilli
48
- clipBoxMin: vec4<f32>, // xyz = clip-box min corner (world), w = pad
49
- clipBoxMax: vec4<f32>, // xyz = clip-box max corner (world), w = pad
50
- quantParams: vec4<f32>, // local min xyz, lattice step w
51
- rteViewProj: mat4x4<f32>, // appended frame; bit 16 selects it
52
- drawableDeltaHigh: vec4<f32>,
53
- drawableDeltaLow: vec4<f32>,
54
- rteCameraHigh: vec4<f32>,
55
- rteCameraLow: vec4<f32>,
56
- }
57
- @binding(0) @group(0) var<uniform> uniforms: Uniforms;
58
- const RTE_DRAWABLE_FLAG: u32 = ${MESH_FLAG_RTE_DRAWABLE}u;
41
+ ${meshUniformsWgsl}
59
42
  ${relativeToEyeWgsl}
60
43
  ${mainRteWgsl}
61
44
  // Shared group(1) lighting; packing matches packEnvironmentUniforms().
@@ -83,6 +66,7 @@ export const mainShaderSource = `
83
66
  const TRANSLUCENT_OPACITY_SCALE: f32 = ${TRANSLUCENT_OPACITY_SCALE};
84
67
  ${colorTransferWgsl}
85
68
  ${specularWgsl}
69
+ ${entityColorTableWgsl}
86
70
 
87
71
  ${mainShadowWgsl}
88
72
 
@@ -97,10 +81,9 @@ export const mainShaderSource = `
97
81
  @location(0) worldPos: vec3<f32>,
98
82
  @location(1) normal: vec3<f32>,
99
83
  @location(2) @interpolate(flat) entityId: u32,
100
- @location(3) viewPos: vec3<f32>, // For edge detection
101
84
  // Per-draw albedo carried from the vertex stage so the fragment shader
102
85
  // is shared by the flat path (vs_main writes uniforms.baseColor — the
103
- // per-batch / overlay-override colour) AND the instanced path
86
+ // per-batch colour) AND the instanced path
104
87
  // (vs_instanced writes the per-occurrence colour from the instance
105
88
  // buffer). For the flat path this is identical to reading
106
89
  // uniforms.baseColor directly (the value is constant across the draw).
@@ -182,7 +165,6 @@ export const mainShaderSource = `
182
165
  output.color = uniforms.baseColor;
183
166
  output.instSelected = 0u;
184
167
  output.eyePos = eyePos;
185
- output.viewPos = select((uniforms.viewProj * worldPos).xyz, (uniforms.rteViewProj * vec4<f32>(eyePos, 1.0)).xyz, rte);
186
168
  return output;
187
169
  }
188
170
 
@@ -210,12 +192,10 @@ export const mainShaderSource = `
210
192
  // an 8-bit MATERIAL-COLOUR salt that mergeGeometry/interleaveTextured
211
193
  // baked into the HIGH 8 bits of the entityId lane (low 24 = picking id,
212
194
  // masked off by encodeId24). Crucially the salt comes from the mesh's
213
- // OWN colour, NOT the per-draw baseColor uniform — so the base opaque
214
- // pass and the lens/IDS/compare/4D OVERLAY pass (which redraws the same
215
- // geometry with a DIFFERENT draw colour) compute the SAME nudge, and
216
- // the overlay pipeline's depthCompare:'equal' matches instead of
217
- // rejecting every fragment. At 1e-6 per step the max world-space offset
218
- // is <3mm at 10m — invisible.
195
+ // OWN colour, NOT the per-draw baseColor uniform — so every redraw of
196
+ // the same geometry with a different draw colour (the selection
197
+ // highlight's greater-equal pass) computes the SAME nudge as its
198
+ // batch. At 1e-6 per step the max world-space offset is <3mm at 10m.
219
199
  let colorSalt = (input.entityId >> 24u) * 2654435761u;
220
200
  let zHash = (((input.entityId & 0x00FFFFFFu) ^ colorSalt) * 2654435761u) & 255u;
221
201
  output.position.z *= 1.0 + f32(zHash) * 1e-6;
@@ -225,8 +205,6 @@ export const mainShaderSource = `
225
205
  output.color = uniforms.baseColor;
226
206
  output.instSelected = 0u; // flat path selects via uniforms.flags.x
227
207
  output.eyePos = eyePos;
228
- // Store view-space position for edge detection
229
- output.viewPos = select((uniforms.viewProj * worldPos).xyz, (uniforms.rteViewProj * vec4<f32>(eyePos, 1.0)).xyz, rte);
230
208
  return output;
231
209
  }
232
210
 
@@ -245,8 +223,8 @@ export const mainShaderSource = `
245
223
  let eyePos = rteInstancePosition(linearLocal, inst.anchorHigh.xyz, inst.anchorLow.xyz).xyz;
246
224
  output.position = uniforms.rteViewProj * vec4<f32>(eyePos, 1.0);
247
225
  // Same per-entity depth nudge as vs_main. No colour salt here: the
248
- // instanced path has no base-vs-overlay coincident redraw (yet), so the
249
- // raw picking id is enough to separate coplanar entities.
226
+ // instanced path never redraws an occurrence with a second draw
227
+ // colour, so the raw picking id is enough to separate coplanar entities.
250
228
  let zHash = ((inst.instEntityId & 0x00FFFFFFu) * 2654435761u) & 255u;
251
229
  output.position.z *= 1.0 + f32(zHash) * 1e-6;
252
230
  output.worldPos = worldPos.xyz;
@@ -255,7 +233,6 @@ export const mainShaderSource = `
255
233
  output.color = inst.instColor;
256
234
  output.instSelected = inst.instSelected;
257
235
  output.eyePos = eyePos;
258
- output.viewPos = (uniforms.rteViewProj * vec4<f32>(eyePos, 1.0)).xyz;
259
236
  return output;
260
237
  }
261
238
 
@@ -278,7 +255,7 @@ export const mainShaderSource = `
278
255
  // that frame; otherwise the RTE vertex precision is thrown away at
279
256
  // section/crop/derivative ingress. Instanced geometry is not yet
280
257
  // anchored, so its worldPos remains the authoritative input.
281
- let fragmentPos = select(input.worldPos, input.eyePos, (uniforms.flags.x & RTE_DRAWABLE_FLAG) != 0u);
258
+ let fragmentPos = clipSpacePos(input.worldPos, input.eyePos);
282
259
  // Per-instance hide/isolate: bit 1 of the instance flags lane marks a hidden
283
260
  // occurrence. Discard it so it neither draws nor writes depth (and the pick
284
261
  // pass applies the same discard, so it isn't pickable). vs_main writes
@@ -300,27 +277,9 @@ export const mainShaderSource = `
300
277
  if (!occOpaque) { discard; }
301
278
  }
302
279
  }
303
- // Section plane clipping - discard fragments ABOVE the plane.
304
- // flags.y packs two bits: bit 0 = enabled, bit 1 = flipped.
305
- let sectionEnabled = (uniforms.flags.y & 1u) == 1u;
306
- if (sectionEnabled) {
307
- let planeNormal = uniforms.sectionPlane.xyz;
308
- let planeDistance = uniforms.sectionPlane.w;
309
- let flipped = (uniforms.flags.y & 2u) == 2u;
310
- let side = select(1.0, -1.0, flipped);
311
- let distToPlane = (dot(fragmentPos, planeNormal) - planeDistance) * side;
312
- if (distToPlane > 0.0) {
313
- discard;
314
- }
315
- }
316
- // Clip box (section / crop box): discard fragments OUTSIDE the AABB.
317
- // flags.y bit 2 = clip-box enabled.
318
- if ((uniforms.flags.y & 4u) != 0u) {
319
- let p = fragmentPos;
320
- if (any(p < uniforms.clipBoxMin.xyz) || any(p > uniforms.clipBoxMax.xyz)) {
321
- discard;
322
- }
323
- }
280
+ // Section plane / clip box (mesh-uniforms.wgsl.ts, shared with the
281
+ // selection mask so an outline is cut where its surface is).
282
+ if (sectionClipped(fragmentPos)) { discard; }
324
283
 
325
284
  // Compute normal via derivative-based flat shading.
326
285
  //
@@ -432,15 +391,9 @@ export const mainShaderSource = `
432
391
  let irradiance = lightTerm * (env.exposure * IRRADIANCE_CALIBRATION);
433
392
  var color = baseColor * irradiance;
434
393
 
435
- // flags.x is a bitfield:
436
- // bit 0 (value 1) = isSelected → selection-highlight + force opaque
437
- // bit 1 (value 2) = isOverlay → color-override pass; preserve
438
- // baseColor.a (overlay pipeline has
439
- // src-alpha blending) AND skip the
440
- // specular term, so an override
441
- // paints its colour, lit, and a
442
- // low-alpha ghost tint does not
443
- // pick up glass reflections.
394
+ // flags.x bit 0 (value 1) = isSelected → selection highlight, forced opaque.
395
+ // bit 1 (value 2) = isOverlay → legacy overlay pipeline callers;
396
+ // preserve alpha and skip specular for their blended draw.
444
397
  // Selected via the per-draw flag (flat path) OR the per-occurrence flag
445
398
  // (instanced path — vs_instanced reads it from the instance buffer).
446
399
  let isSelected = ((uniforms.flags.x & 1u) == 1u) || ((input.instSelected & 1u) == 1u);
@@ -480,28 +433,22 @@ export const mainShaderSource = `
480
433
  color = selectionColor.rgb * shade;
481
434
  }
482
435
 
483
- // flags.x bit 5 (value 32) = EMPHASIZE overlay: render the colour
484
- // override FULLY UNLIT and saturated (no lighting attenuation, no
485
- // wash-to-white) so the focused clash pair reads as a solid, vivid,
486
- // distinct colour that pops against the lit model — like a clash tool.
487
- // A faint normal-based shade keeps the silhouette from going flat. (#1277)
436
+ // Public getOverlayPipeline() callers can still use the emphasized
437
+ // overlay bit even though internal colour overrides now use a table.
488
438
  let emphasizedOverlay = isOverlay && (uniforms.flags.x & 32u) != 0u;
489
439
  if (emphasizedOverlay) {
490
440
  let facet = 0.85 + 0.15 * abs(dot(N, normalize(vec3<f32>(0.3, 1.0, 0.2))));
491
441
  color = baseColor * facet;
492
442
  }
493
443
 
494
- // Force alpha to 1.0 for selected objects so the highlight is fully
495
- // opaque (the selection pipeline has no alpha blending). Emphasized
496
- // clash overlay paints a SOLID vivid fill (force opaque) so it isn't
497
- // blended down to a pale tint against the geometry beneath.
444
+ // Selected objects and emphasized overlays draw fully opaque.
498
445
  var finalAlpha = select(input.color.a, 1.0, isSelected || emphasizedOverlay);
499
446
 
500
447
  // Specular (#5386; specular.wgsl.ts), after the diffuse above. Not
501
- // on the selection highlight (it would wash the blue out) nor on a
502
- // colour-override overlay. The sun lobe is scaled exactly like the
503
- // diffuse sun term, and both terms like irradiance, so a preset's
504
- // highlights and its diffuse light move together.
448
+ // on the selection highlight or legacy overlay (it would wash out
449
+ // their colours); a table override is composited without it below. The
450
+ // sun lobe is scaled exactly like the diffuse sun term, and both
451
+ // terms like irradiance, so a preset's highlights and diffuse move together.
505
452
  if (!isSelected && !isOverlay) {
506
453
  // Glass is an authored translucent material (mesh-material.ts
507
454
  // also gives it its smooth roughness). An X-Ray or compare fade
@@ -511,13 +458,23 @@ export const mainShaderSource = `
511
458
  let instancedPass = (uniforms.flags.x & 4u) != 0u;
512
459
  let translucent = finalAlpha < 0.99;
513
460
  let glass = translucent && uniforms.transmission.x > 0.5 && !instancedPass;
514
- let metallic = clamp(uniforms.metallicRoughness.x, 0.0, 1.0);
461
+ // #5984: an instanced occurrence's own authored finish rides its
462
+ // flags lane (instanced-render.ts packInstanceFinish); the pass's
463
+ // uniform row is the shared default.
464
+ var metallicRoughness = vec2<f32>(uniforms.metallicRoughness.x, uniforms.metallicRoughness.y);
465
+ if (instancedPass && (input.instSelected & 4u) != 0u) {
466
+ metallicRoughness.x = f32((input.instSelected >> 16u) & 255u) / 255.0;
467
+ }
468
+ if (instancedPass && (input.instSelected & 8u) != 0u) {
469
+ metallicRoughness.y = f32((input.instSelected >> 24u) & 255u) / 255.0;
470
+ }
471
+ let metallic = clamp(metallicRoughness.x, 0.0, 1.0);
515
472
  let spec = surfaceSpecular(
516
473
  N,
517
474
  normalize(-input.eyePos),
518
475
  baseColor,
519
476
  metallic,
520
- uniforms.metallicRoughness.y,
477
+ metallicRoughness.y,
521
478
  env.sunColor * (env.sunIntensity * sunShadow),
522
479
  );
523
480
  // What the lobe reflects is not there to diffuse; a metal has no
@@ -552,41 +509,16 @@ export const mainShaderSource = `
552
509
  // the roll-off threshold.
553
510
  color = neutralCompress(color);
554
511
 
555
- // Subtle edge enhancement using screen-space derivatives.
556
- //
557
- // Use the SHADED normal (face normal from dpdx/dpdy above)
558
- // for the normal-gradient term, not the interpolated vertex
559
- // normal — otherwise we get spurious dark stripes on flat
560
- // surfaces whose vertex normals carry numerical noise from
561
- // CSG output (the visible scar-line symptom would just
562
- // resurface here even after the lit-normal fix). With the
563
- // face normal, coplanar adjacent triangles agree exactly →
564
- // zero normal gradient → no false edge; only the genuine
565
- // creases between perpendicular faces produce a real
566
- // gradient and get the intended outline.
567
- let depthGradient = length(vec2<f32>(
568
- dpdx(input.viewPos.z),
569
- dpdy(input.viewPos.z)
570
- ));
571
- let normalGradient = length(vec2<f32>(
572
- length(dpdx(N)),
573
- length(dpdy(N))
574
- ));
575
-
576
- if (uniforms.flags.z == 1u) {
577
- // Threshold filters subtle normal discontinuities at internal
578
- // triangle edges between coplanar entities in the same batch.
579
- let edgeFactor = smoothstep(0.02, 0.12, depthGradient * 10.0 + normalGradient * 5.0);
580
- let edgeIntensity = f32(uniforms.flags.w) / 1000.0;
581
- let edgeDarkenStrength = clamp(0.25 * edgeIntensity, 0.0, 0.85);
582
- let edgeDarken = mix(1.0, 1.0 - edgeDarkenStrength, edgeFactor);
583
- color *= edgeDarken;
584
- }
585
-
586
512
  color = linearToSrgb(clamp(color, vec3<f32>(0.0), vec3<f32>(1.0)));
587
513
 
588
514
  var out: FragmentOutput;
589
515
  out.color = vec4<f32>(color, finalAlpha);
516
+ // Colour override from the per-entity table (#6076): only draws the
517
+ // renderer marks (overrideParams.y), i.e. depth-writing opaque ones.
518
+ let entityOverride = entityOverrideColor(input.entityId);
519
+ if (entityOverride.a >= 0.0 && !isSelected) {
520
+ out.color = paintEntityOverride(out.color, entityOverride, irradiance, N);
521
+ }
590
522
  out.objectIdEncoded = encodeId24(input.entityId);
591
523
  return out;
592
524
  }
@@ -1 +1 @@
1
- {"version":3,"file":"main.wgsl.js","sourceRoot":"","sources":["../../src/shaders/main.wgsl.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;;;GAMG;AACH,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD;;;;GAIG;AACH,MAAM,yBAAyB,GAAG,GAAG,CAAC;AAEtC;;;;;;;;;;;;;;;GAeG;AACH,MAAM,sBAAsB,GAAG,IAAI,CAAC;AAEpC,MAAM,CAAC,MAAM,gBAAgB,GAAG;;;;;;;;;;;;;;;;;;;yCAmBS,sBAAsB;UACrD,iBAAiB;UACjB,WAAW;;;;;;;;;;;;;;;;;;;;;;8CAsByB,sBAAsB;iDACnB,yBAAyB;UAChE,iBAAiB;UACjB,YAAY;;UAEZ,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA0fjB,CAAC"}
1
+ {"version":3,"file":"main.wgsl.js","sourceRoot":"","sources":["../../src/shaders/main.wgsl.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;;;GAMG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAEpE;;;;GAIG;AACH,MAAM,yBAAyB,GAAG,GAAG,CAAC;AAEtC;;;;;;;;;;;;;;;GAeG;AACH,MAAM,sBAAsB,GAAG,IAAI,CAAC;AAEpC,MAAM,CAAC,MAAM,gBAAgB,GAAG;UACtB,gBAAgB;UAChB,iBAAiB;UACjB,WAAW;;;;;;;;;;;;;;;;;;;;;;8CAsByB,sBAAsB;iDACnB,yBAAyB;UAChE,iBAAiB;UACjB,YAAY;UACZ,oBAAoB;;UAEpB,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAscjB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare const meshUniformsWgsl = "\n struct Uniforms {\n viewProj: mat4x4<f32>,\n model: mat4x4<f32>,\n baseColor: vec4<f32>,\n metallicRoughness: vec2<f32>, // x = metallic, y = roughness (mesh-material.ts)\n transmission: vec2<f32>, // x = 1: authored translucent, drawn as glass; y = pad\n sectionPlane: vec4<f32>, // xyz = plane normal, w = plane distance\n flags: vec4<u32>, // x = isSelected, y = section/clip bits; z, w unused (0) since #5746 dropped the derivative edges\n clipBoxMin: vec4<f32>, // xyz = clip-box min corner (world), w = pad\n clipBoxMax: vec4<f32>, // xyz = clip-box max corner (world), w = pad\n quantParams: vec4<f32>, // local min xyz, lattice step w\n rteViewProj: mat4x4<f32>, // appended frame; bit 16 selects it\n drawableDeltaHigh: vec4<f32>,\n drawableDeltaLow: vec4<f32>,\n rteCameraHigh: vec4<f32>,\n rteCameraLow: vec4<f32>,\n overrideParams: vec4<u32>, // x = draw's id anchor, y = OVERRIDE_* bits (entity-color-table.ts, #6076)\n }\n @binding(0) @group(0) var<uniform> uniforms: Uniforms;\n const RTE_DRAWABLE_FLAG: u32 = 65536u;\n\n // The fragment position the section/crop tests run in: camera-relative\n // for RTE drawables (the packer writes plane and box in that frame),\n // world space otherwise.\n fn clipSpacePos(worldPos: vec3<f32>, eyePos: vec3<f32>) -> vec3<f32> {\n return select(worldPos, eyePos, (uniforms.flags.x & RTE_DRAWABLE_FLAG) != 0u);\n }\n\n // True when p is cut away: ABOVE the section plane (flags.y bit 0 =\n // enabled, bit 1 = flipped) or OUTSIDE the clip box (bit 2).\n fn sectionClipped(p: vec3<f32>) -> bool {\n if ((uniforms.flags.y & 1u) == 1u) {\n let side = select(1.0, -1.0, (uniforms.flags.y & 2u) == 2u);\n if ((dot(p, uniforms.sectionPlane.xyz) - uniforms.sectionPlane.w) * side > 0.0) { return true; }\n }\n if ((uniforms.flags.y & 4u) != 0u) {\n if (any(p < uniforms.clipBoxMin.xyz) || any(p > uniforms.clipBoxMax.xyz)) { return true; }\n }\n return false;\n }\n";
2
+ //# sourceMappingURL=mesh-uniforms.wgsl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mesh-uniforms.wgsl.d.ts","sourceRoot":"","sources":["../../src/shaders/mesh-uniforms.wgsl.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,gBAAgB,ouEAyC5B,CAAC"}
@@ -0,0 +1,53 @@
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
+ /**
5
+ * The per-mesh uniform block (`@group(0) @binding(0)`) and the section /
6
+ * clip-box test that reads it, shared by `main.wgsl.ts` and the selection
7
+ * mask fragment stage (#5390) so an outline is cut exactly where the
8
+ * surface it traces is. Packing: `mesh-rte-uniforms.ts` / `index.ts`.
9
+ */
10
+ import { MESH_FLAG_RTE_DRAWABLE } from '../mesh-rte-uniforms.js';
11
+ export const meshUniformsWgsl = `
12
+ struct Uniforms {
13
+ viewProj: mat4x4<f32>,
14
+ model: mat4x4<f32>,
15
+ baseColor: vec4<f32>,
16
+ metallicRoughness: vec2<f32>, // x = metallic, y = roughness (mesh-material.ts)
17
+ transmission: vec2<f32>, // x = 1: authored translucent, drawn as glass; y = pad
18
+ sectionPlane: vec4<f32>, // xyz = plane normal, w = plane distance
19
+ flags: vec4<u32>, // x = isSelected, y = section/clip bits; z, w unused (0) since #5746 dropped the derivative edges
20
+ clipBoxMin: vec4<f32>, // xyz = clip-box min corner (world), w = pad
21
+ clipBoxMax: vec4<f32>, // xyz = clip-box max corner (world), w = pad
22
+ quantParams: vec4<f32>, // local min xyz, lattice step w
23
+ rteViewProj: mat4x4<f32>, // appended frame; bit 16 selects it
24
+ drawableDeltaHigh: vec4<f32>,
25
+ drawableDeltaLow: vec4<f32>,
26
+ rteCameraHigh: vec4<f32>,
27
+ rteCameraLow: vec4<f32>,
28
+ overrideParams: vec4<u32>, // x = draw's id anchor, y = OVERRIDE_* bits (entity-color-table.ts, #6076)
29
+ }
30
+ @binding(0) @group(0) var<uniform> uniforms: Uniforms;
31
+ const RTE_DRAWABLE_FLAG: u32 = ${MESH_FLAG_RTE_DRAWABLE}u;
32
+
33
+ // The fragment position the section/crop tests run in: camera-relative
34
+ // for RTE drawables (the packer writes plane and box in that frame),
35
+ // world space otherwise.
36
+ fn clipSpacePos(worldPos: vec3<f32>, eyePos: vec3<f32>) -> vec3<f32> {
37
+ return select(worldPos, eyePos, (uniforms.flags.x & RTE_DRAWABLE_FLAG) != 0u);
38
+ }
39
+
40
+ // True when p is cut away: ABOVE the section plane (flags.y bit 0 =
41
+ // enabled, bit 1 = flipped) or OUTSIDE the clip box (bit 2).
42
+ fn sectionClipped(p: vec3<f32>) -> bool {
43
+ if ((uniforms.flags.y & 1u) == 1u) {
44
+ let side = select(1.0, -1.0, (uniforms.flags.y & 2u) == 2u);
45
+ if ((dot(p, uniforms.sectionPlane.xyz) - uniforms.sectionPlane.w) * side > 0.0) { return true; }
46
+ }
47
+ if ((uniforms.flags.y & 4u) != 0u) {
48
+ if (any(p < uniforms.clipBoxMin.xyz) || any(p > uniforms.clipBoxMax.xyz)) { return true; }
49
+ }
50
+ return false;
51
+ }
52
+ `;
53
+ //# sourceMappingURL=mesh-uniforms.wgsl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mesh-uniforms.wgsl.js","sourceRoot":"","sources":["../../src/shaders/mesh-uniforms.wgsl.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;;GAKG;AACH,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAEjE,MAAM,CAAC,MAAM,gBAAgB,GAAG;;;;;;;;;;;;;;;;;;;;yCAoBS,sBAAsB;;;;;;;;;;;;;;;;;;;;;CAqB9D,CAAC"}
@@ -85,6 +85,7 @@ export declare const SECTION_2D_UNIFORM_SLOT_INDEX: {
85
85
  readonly grid: number;
86
86
  readonly dxf: number;
87
87
  readonly terrain: number;
88
+ readonly centreline: number;
88
89
  readonly clashBox: number;
89
90
  };
90
91
  /** One cap record plus a partition range for every line family. */
@@ -1 +1 @@
1
- {"version":3,"file":"section-2d-overlay.wgsl.d.ts","sourceRoot":"","sources":["../../src/shaders/section-2d-overlay.wgsl.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;GAcG;AAEH;;;;;;;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;;;;;;;;CAQrE,CAAC;AAEX,mEAAmE;AACnE,eAAO,MAAM,6BAA6B,QAAyC,CAAC;AAEpF;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAIlE;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,wBAAwB,k7LA8H9B,CAAC;AAER;;;GAGG;AACH,eAAO,MAAM,4BAA4B,46EAyDlC,CAAC"}
1
+ {"version":3,"file":"section-2d-overlay.wgsl.d.ts","sourceRoot":"","sources":["../../src/shaders/section-2d-overlay.wgsl.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;GAcG;AAEH;;;;;;;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;;;;;;;;;CASrE,CAAC;AAEX,mEAAmE;AACnE,eAAO,MAAM,6BAA6B,QAAyC,CAAC;AAEpF;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAIlE;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,wBAAwB,k7LA8H9B,CAAC;AAER;;;GAGG;AACH,eAAO,MAAM,4BAA4B,46EAyDlC,CAAC"}
@@ -88,10 +88,11 @@ export const SECTION_2D_UNIFORM_SLOT_INDEX = {
88
88
  grid: 1 + SECTION_2D_MAX_LINE_PARTITIONS * 2,
89
89
  dxf: 1 + SECTION_2D_MAX_LINE_PARTITIONS * 3,
90
90
  terrain: 1 + SECTION_2D_MAX_LINE_PARTITIONS * 4,
91
- clashBox: 1 + SECTION_2D_MAX_LINE_PARTITIONS * 5,
91
+ centreline: 1 + SECTION_2D_MAX_LINE_PARTITIONS * 5,
92
+ clashBox: 1 + SECTION_2D_MAX_LINE_PARTITIONS * 6,
92
93
  };
93
94
  /** One cap record plus a partition range for every line family. */
94
- export const SECTION_2D_UNIFORM_SLOT_COUNT = 1 + SECTION_2D_MAX_LINE_PARTITIONS * 6;
95
+ export const SECTION_2D_UNIFORM_SLOT_COUNT = 1 + SECTION_2D_MAX_LINE_PARTITIONS * 7;
95
96
  /**
96
97
  * Byte stride between uniform slots for `device`.
97
98
  *
@@ -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;;;;;;;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,QAAQ,EAAE,CAAC,GAAG,8BAA8B,GAAG,CAAC;CACxC,CAAC;AAEX,mEAAmE;AACnE,MAAM,CAAC,MAAM,6BAA6B,GAAG,CAAC,GAAG,8BAA8B,GAAG,CAAC,CAAC;AAEpF;;;;;;;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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8H5C,CAAC;AAER;;;GAGG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAyDhD,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;;;;;;;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;CACxC,CAAC;AAEX,mEAAmE;AACnE,MAAM,CAAC,MAAM,6BAA6B,GAAG,CAAC,GAAG,8BAA8B,GAAG,CAAC,CAAC;AAEpF;;;;;;;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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8H5C,CAAC;AAER;;;GAGG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAyDhD,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Bind-group index of the scene depth texture in the mask pipeline layout:
3
+ * group 0 is the mesh uniform that `vs_main` reads, so depth is group 1.
4
+ * `selection-mask-pass.ts` builds its layout from this same constant.
5
+ */
6
+ export declare const SELECTION_MASK_DEPTH_GROUP = 1;
7
+ /**
8
+ * Bind-group index of the hovered-entity uniform (`vec4<u32>`, x = id) in the
9
+ * INSTANCED mask pipeline layout only (#5745). The non-instanced variant
10
+ * selects its meshes on the CPU and declares no such binding.
11
+ */
12
+ export declare const SELECTION_MASK_HOVER_GROUP = 2;
13
+ /** Relative depth slack for "the same surface" (reverse-Z: ~0.2 % of the distance). */
14
+ export declare const MASK_DEPTH_REL_TOLERANCE = 0.002;
15
+ export declare function selectionMaskFragmentSource(multisampled: boolean, instanced?: boolean): string;
16
+ //# sourceMappingURL=selection-mask.wgsl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selection-mask.wgsl.d.ts","sourceRoot":"","sources":["../../src/shaders/selection-mask.wgsl.ts"],"names":[],"mappings":"AAqDA;;;;GAIG;AACH,eAAO,MAAM,0BAA0B,IAAI,CAAC;AAE5C;;;;GAIG;AACH,eAAO,MAAM,0BAA0B,IAAI,CAAC;AAE5C,uFAAuF;AACvF,eAAO,MAAM,wBAAwB,QAAO,CAAC;AAuB7C,wBAAgB,2BAA2B,CAAC,YAAY,EAAE,OAAO,EAAE,SAAS,UAAQ,GAAG,MAAM,CAmD5F"}
@@ -0,0 +1,138 @@
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
+ /**
5
+ * Fragment stage for the selection/hover mask pass (#5390). The vertex
6
+ * stage is `mainShaderSource`'s `vs_main` (recompiled as its own
7
+ * `GPUShaderModule`, reusing the exact same RTE/section-plane/transform
8
+ * math the selection highlight draw already uses) — this module supplies
9
+ * only the fragment side, so the two can be mixed in one pipeline: a
10
+ * fragment entry point with no declared inputs is interface-compatible
11
+ * with any vertex stage, since `@builtin(position)` is always available.
12
+ *
13
+ * Manual depth test instead of a real depth-stencil attachment: comparing
14
+ * `@builtin(position).z` (this fragment's own device depth) against the
15
+ * scene depth texture (`depth-reconstruct.wgsl.ts`'s `loadDepth`, same
16
+ * sample-0 convention as the AO/edge passes) means the mask targets stay
17
+ * single-sample regardless of the scene's MSAA sample count, with no
18
+ * resolve step of their own.
19
+ *
20
+ * Every output first drops fragments the section plane or clip box cuts
21
+ * away (`sectionClipped`, shared with `fs_main`), so an outline never traces
22
+ * geometry the section tool removed. That reads the mesh uniform at group 0
23
+ * and the `worldPos` / `eyePos` varyings `vs_main` already emits.
24
+ *
25
+ * Three outputs, at most one per pipeline:
26
+ * - `fs_mask_selected_visible` / `fs_mask_hover_visible`: pass only where
27
+ * this fragment is at least as close as the stored scene depth
28
+ * (reverse-Z: `>=`), i.e. not occluded. `discard` otherwise, so the
29
+ * additive blend leaves the target at 0 there. The comparison is NOT
30
+ * exact: the stored depth is sample 0 of an MSAA attachment (not the
31
+ * pixel centre) and was usually written by the batch draw, whose
32
+ * positions may be quantized, so the same surface lands a slope-sized
33
+ * step away. `isVisible` therefore allows this fragment's own depth
34
+ * slope (`fwidth`, taken before any branch so it stays in uniform
35
+ * control flow) plus `MASK_DEPTH_REL_TOLERANCE` of its depth; reverse-Z
36
+ * depth is proportional to 1/distance, so that is a relative distance
37
+ * tolerance. A fixed 1e-7 epsilon failed on every pixel of a real
38
+ * WebGPU frame, leaving the visible mask empty.
39
+ * - `fs_mask_selected_all`: always passes (drawn "through occluders").
40
+ *
41
+ * Instanced variant (#5745, `instanced = true`): the vertex stage is
42
+ * `vs_instanced` instead, so one draw covers every occurrence of a template.
43
+ * The same entry points then also keep only the occurrences they are for:
44
+ * selected ones (instance flag bit 0) for the selected outputs, and the one
45
+ * whose entity id is the hovered id (a small uniform at
46
+ * `SELECTION_MASK_HOVER_GROUP`) for the hover output. Hidden occurrences
47
+ * (flag bit 1) are dropped, as `fs_main` drops them. The section/clip cut and
48
+ * the depth test are the same code as the non-instanced variant.
49
+ */
50
+ import { depthTextureWgsl } from './depth-reconstruct.wgsl.js';
51
+ import { meshUniformsWgsl } from './mesh-uniforms.wgsl.js';
52
+ /**
53
+ * Bind-group index of the scene depth texture in the mask pipeline layout:
54
+ * group 0 is the mesh uniform that `vs_main` reads, so depth is group 1.
55
+ * `selection-mask-pass.ts` builds its layout from this same constant.
56
+ */
57
+ export const SELECTION_MASK_DEPTH_GROUP = 1;
58
+ /**
59
+ * Bind-group index of the hovered-entity uniform (`vec4<u32>`, x = id) in the
60
+ * INSTANCED mask pipeline layout only (#5745). The non-instanced variant
61
+ * selects its meshes on the CPU and declares no such binding.
62
+ */
63
+ export const SELECTION_MASK_HOVER_GROUP = 2;
64
+ /** Relative depth slack for "the same surface" (reverse-Z: ~0.2 % of the distance). */
65
+ export const MASK_DEPTH_REL_TOLERANCE = 2e-3;
66
+ /**
67
+ * Which occurrences each output keeps. The non-instanced pass is handed only
68
+ * the meshes it should draw, so every fragment qualifies; the instanced pass
69
+ * draws whole templates and filters per occurrence off the flat varyings
70
+ * `vs_instanced` emits (`VertexOutput.entityId` / `.instSelected`).
71
+ */
72
+ function occurrenceFilterWgsl(instanced) {
73
+ if (!instanced) {
74
+ return `
75
+ fn isSelectedOccurrence(input: MaskInput) -> bool { return true; }
76
+ fn isHoveredOccurrence(input: MaskInput) -> bool { return true; }`;
77
+ }
78
+ return `
79
+ @group(${SELECTION_MASK_HOVER_GROUP}) @binding(0) var<uniform> maskHover: vec4<u32>;
80
+ // bit 0 = selected, bit 1 = hidden (instanced-vertex-layout.ts).
81
+ fn isSelectedOccurrence(input: MaskInput) -> bool { return (input.instSelected & 3u) == 1u; }
82
+ fn isHoveredOccurrence(input: MaskInput) -> bool {
83
+ return (input.instSelected & 2u) == 0u && input.entityId == maskHover.x;
84
+ }`;
85
+ }
86
+ export function selectionMaskFragmentSource(multisampled, instanced = false) {
87
+ const instanceVaryings = instanced
88
+ ? `
89
+ @location(2) @interpolate(flat) entityId: u32,
90
+ @location(5) @interpolate(flat) instSelected: u32,`
91
+ : '';
92
+ return `
93
+ ${meshUniformsWgsl}
94
+ ${depthTextureWgsl(0, multisampled, SELECTION_MASK_DEPTH_GROUP)}
95
+
96
+ // The subset of the vertex stage's VertexOutput the mask reads (locations match).
97
+ struct MaskInput {
98
+ @builtin(position) fragPos: vec4<f32>,
99
+ @location(0) worldPos: vec3<f32>,
100
+ @location(6) eyePos: vec3<f32>,${instanceVaryings}
101
+ }
102
+ ${occurrenceFilterWgsl(instanced)}
103
+
104
+ fn isCut(input: MaskInput) -> bool {
105
+ return sectionClipped(clipSpacePos(input.worldPos, input.eyePos));
106
+ }
107
+
108
+ const MASK_DEPTH_REL_TOLERANCE: f32 = ${MASK_DEPTH_REL_TOLERANCE};
109
+
110
+ // depthSlope = fwidth(fragPos.z), taken by the caller in uniform control flow.
111
+ fn isVisible(fragPos: vec4<f32>, depthSlope: f32) -> bool {
112
+ let ip = vec2<i32>(fragPos.xy);
113
+ let slack = depthSlope + fragPos.z * MASK_DEPTH_REL_TOLERANCE;
114
+ return fragPos.z >= loadDepth(ip) - slack;
115
+ }
116
+
117
+ @fragment
118
+ fn fs_mask_selected_visible(input: MaskInput) -> @location(0) vec4<f32> {
119
+ let depthSlope = fwidth(input.fragPos.z);
120
+ if (!isSelectedOccurrence(input) || isCut(input) || !isVisible(input.fragPos, depthSlope)) { discard; }
121
+ return vec4<f32>(1.0, 0.0, 0.0, 0.0);
122
+ }
123
+
124
+ @fragment
125
+ fn fs_mask_hover_visible(input: MaskInput) -> @location(0) vec4<f32> {
126
+ let depthSlope = fwidth(input.fragPos.z);
127
+ if (!isHoveredOccurrence(input) || isCut(input) || !isVisible(input.fragPos, depthSlope)) { discard; }
128
+ return vec4<f32>(0.0, 1.0, 0.0, 0.0);
129
+ }
130
+
131
+ @fragment
132
+ fn fs_mask_selected_all(input: MaskInput) -> @location(0) vec4<f32> {
133
+ if (!isSelectedOccurrence(input) || isCut(input)) { discard; }
134
+ return vec4<f32>(1.0, 0.0, 0.0, 0.0);
135
+ }
136
+ `;
137
+ }
138
+ //# sourceMappingURL=selection-mask.wgsl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selection-mask.wgsl.js","sourceRoot":"","sources":["../../src/shaders/selection-mask.wgsl.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAE3D;;;;GAIG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,CAAC;AAE5C;;;;GAIG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,CAAC;AAE5C,uFAAuF;AACvF,MAAM,CAAC,MAAM,wBAAwB,GAAG,IAAI,CAAC;AAE7C;;;;;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,YAAqB,EAAE,SAAS,GAAG,KAAK;IAClF,MAAM,gBAAgB,GAAG,SAAS;QAChC,CAAC,CAAC;;6DAEuD;QACzD,CAAC,CAAC,EAAE,CAAC;IACP,OAAO;UACC,gBAAgB;UAChB,gBAAgB,CAAC,CAAC,EAAE,YAAY,EAAE,0BAA0B,CAAC;;;;;;2CAM5B,gBAAgB;;UAEjD,oBAAoB,CAAC,SAAS,CAAC;;;;;;gDAMO,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4BvE,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 unused by\n // the depth pass and left unbound.\n @location(9) flags: u32,\n // V2 split drawable-minus-camera delta, byte offsets 88 and 104.\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>) -> ShadowOut {\n var out: ShadowOut;\n out.position = light.lightViewProj * eyePos;\n out.worldPos = eyePos.xyz;\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)));\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)));\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 )));\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)));\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>) {\n if ((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 ((clip.flags.x & 4u) != 0u) {\n if (any(worldPos < clip.clipBoxMin.xyz) || any(worldPos > clip.clipBoxMax.xyz)) {\n discard;\n }\n }\n }\n";
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>) -> ShadowOut {\n var out: ShadowOut;\n out.position = light.lightViewProj * eyePos;\n out.worldPos = eyePos.xyz;\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)));\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)));\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 )));\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)));\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>) {\n if ((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 ((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":"AAMA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,kBAAkB,+6LA4H9B,CAAC"}
1
+ {"version":3,"file":"shadow.wgsl.d.ts","sourceRoot":"","sources":["../../src/shaders/shadow.wgsl.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,kBAAkB,w7LA4H9B,CAAC"}
@@ -75,10 +75,10 @@ export const shadowShaderSource = `
75
75
  @location(5) m2: vec4<f32>,
76
76
  @location(6) m3: vec4<f32>,
77
77
  // Per-occurrence flags lane (bit 1 = hidden), same instance buffer the
78
- // colour pass reads. Locations 7 (entityId) and 8 (rgba) are unused by
79
- // the depth pass and left unbound.
78
+ // colour pass reads. Locations 7 (entityId) and 8 (rgba) are in the
79
+ // shared layout but unused by the depth pass.
80
80
  @location(9) flags: u32,
81
- // V2 split drawable-minus-camera delta, byte offsets 88 and 104.
81
+ // Split drawable-minus-camera delta from the slot-2 delta stream.
82
82
  @location(10) anchorHigh: vec4<f32>,
83
83
  @location(11) anchorLow: vec4<f32>,
84
84
  }