@forgeax/engine-render 0.1.33 → 0.1.35

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 (624) hide show
  1. package/README.md +1062 -127
  2. package/dist/assembly/backend-contract.d.ts +2 -0
  3. package/dist/assembly/backend-contract.d.ts.map +1 -1
  4. package/dist/assembly/barrel-distortion-inspection.d.ts +3 -0
  5. package/dist/assembly/barrel-distortion-inspection.d.ts.map +1 -0
  6. package/dist/assembly/canvas-draw-guard.d.ts +3 -0
  7. package/dist/assembly/canvas-draw-guard.d.ts.map +1 -0
  8. package/dist/assembly/cloud-shadow-entry.d.ts +4 -0
  9. package/dist/assembly/cloud-shadow-entry.d.ts.map +1 -0
  10. package/dist/assembly/device-feature-admission.d.ts +2 -2
  11. package/dist/assembly/dynamic-geometry-host.d.ts +4 -1
  12. package/dist/assembly/dynamic-geometry-host.d.ts.map +1 -1
  13. package/dist/assembly/dynamic-geometry-runtime.d.ts +1 -1
  14. package/dist/assembly/dynamic-geometry-runtime.d.ts.map +1 -1
  15. package/dist/assembly/factory.d.ts +1 -1
  16. package/dist/assembly/factory.d.ts.map +1 -1
  17. package/dist/assembly/host-contract.d.ts +6 -5
  18. package/dist/assembly/host-contract.d.ts.map +1 -1
  19. package/dist/assembly/material/assembly.d.ts.map +1 -1
  20. package/dist/assembly/material/pipeline-helpers.d.ts +5 -0
  21. package/dist/assembly/material/pipeline-helpers.d.ts.map +1 -1
  22. package/dist/assembly/material-shader-policy.d.ts +16 -2
  23. package/dist/assembly/material-shader-policy.d.ts.map +1 -1
  24. package/dist/assembly/material-shader-prewarm.d.ts.map +1 -1
  25. package/dist/assembly/recovery/device-loss-fanout.d.ts.map +1 -1
  26. package/dist/assembly/recovery/renderer-recover.d.ts +2 -0
  27. package/dist/assembly/recovery/renderer-recover.d.ts.map +1 -1
  28. package/dist/assembly/render-feature-post-processes.d.ts +6 -0
  29. package/dist/assembly/render-feature-post-processes.d.ts.map +1 -0
  30. package/dist/assembly/render-target-host.d.ts +5 -0
  31. package/dist/assembly/render-target-host.d.ts.map +1 -1
  32. package/dist/assembly/renderer-facade.d.ts.map +1 -1
  33. package/dist/assembly/renderer-feature-inspection.d.ts +1 -1
  34. package/dist/assembly/renderer-feature-inspection.d.ts.map +1 -1
  35. package/dist/assembly/renderer-frame-transaction.d.ts +12 -0
  36. package/dist/assembly/renderer-frame-transaction.d.ts.map +1 -1
  37. package/dist/assembly/renderer-inspection.d.ts +11 -0
  38. package/dist/assembly/renderer-inspection.d.ts.map +1 -1
  39. package/dist/assembly/webgpu-pbr-ready.d.ts +1 -0
  40. package/dist/assembly/webgpu-pbr-ready.d.ts.map +1 -1
  41. package/dist/assembly/webgpu-ready-per-pass.d.ts +39 -0
  42. package/dist/assembly/webgpu-ready-per-pass.d.ts.map +1 -0
  43. package/dist/assembly/webgpu-ready.d.ts +1 -1
  44. package/dist/assembly/webgpu-ready.d.ts.map +1 -1
  45. package/dist/assembly/webgpu-renderer-bootstrap.d.ts.map +1 -1
  46. package/dist/assembly/webgpu-renderer-observation-owner.d.ts +35 -0
  47. package/dist/assembly/webgpu-renderer-observation-owner.d.ts.map +1 -0
  48. package/dist/assembly/webgpu-renderer.d.ts +2 -1
  49. package/dist/assembly/webgpu-renderer.d.ts.map +1 -1
  50. package/dist/authoring.mjs +2 -4
  51. package/dist/authoring.mjs.map +1 -1
  52. package/dist/barrel-distortion.d.ts +47 -0
  53. package/dist/barrel-distortion.d.ts.map +1 -0
  54. package/dist/chunk-3XUBWRY2.mjs +1842 -0
  55. package/dist/chunk-3XUBWRY2.mjs.map +1 -0
  56. package/dist/{chunk-OO3DRYK7.mjs → chunk-G6HDNGY7.mjs} +2400 -1767
  57. package/dist/chunk-G6HDNGY7.mjs.map +1 -0
  58. package/dist/{chunk-ZDQWGQW7.mjs → chunk-LM5IE7GF.mjs} +4 -4
  59. package/dist/{chunk-ZDQWGQW7.mjs.map → chunk-LM5IE7GF.mjs.map} +1 -1
  60. package/dist/{chunk-VOZWDM3L.mjs → chunk-LNIUIKQ7.mjs} +1514 -498
  61. package/dist/chunk-LNIUIKQ7.mjs.map +1 -0
  62. package/dist/chunk-MXTWKZO4.mjs +3268 -0
  63. package/dist/chunk-MXTWKZO4.mjs.map +1 -0
  64. package/dist/chunk-MZ4C7EHF.mjs +6942 -0
  65. package/dist/chunk-MZ4C7EHF.mjs.map +1 -0
  66. package/dist/chunk-RIPAEGI6.mjs +48 -0
  67. package/dist/chunk-RIPAEGI6.mjs.map +1 -0
  68. package/dist/{chunk-LEAXOMYJ.mjs → chunk-SGPUJW3F.mjs} +117 -5
  69. package/dist/chunk-SGPUJW3F.mjs.map +1 -0
  70. package/dist/{chunk-PA5DRJNI.mjs → chunk-X2KA6WHM.mjs} +29 -4
  71. package/dist/chunk-X2KA6WHM.mjs.map +1 -0
  72. package/dist/{chunk-BLXGONR2.mjs → chunk-XSFB7XXQ.mjs} +51 -4
  73. package/dist/chunk-XSFB7XXQ.mjs.map +1 -0
  74. package/dist/cloud/density.d.ts +55 -0
  75. package/dist/cloud/density.d.ts.map +1 -0
  76. package/dist/cloud/extract.d.ts +25 -0
  77. package/dist/cloud/extract.d.ts.map +1 -0
  78. package/dist/cloud/feature.d.ts +57 -0
  79. package/dist/cloud/feature.d.ts.map +1 -0
  80. package/dist/cloud/frame-context.d.ts +8 -0
  81. package/dist/cloud/frame-context.d.ts.map +1 -0
  82. package/dist/cloud/inspection.d.ts +59 -0
  83. package/dist/cloud/inspection.d.ts.map +1 -0
  84. package/dist/cloud/optics.d.ts +56 -0
  85. package/dist/cloud/optics.d.ts.map +1 -0
  86. package/dist/cloud/parameters.d.ts +73 -0
  87. package/dist/cloud/parameters.d.ts.map +1 -0
  88. package/dist/cloud/resources.d.ts +60 -0
  89. package/dist/cloud/resources.d.ts.map +1 -0
  90. package/dist/cloud/shadow.d.ts +42 -0
  91. package/dist/cloud/shadow.d.ts.map +1 -0
  92. package/dist/cloud/temporal.d.ts +74 -0
  93. package/dist/cloud/temporal.d.ts.map +1 -0
  94. package/dist/components/barrel-distortion.d.ts +13 -0
  95. package/dist/components/barrel-distortion.d.ts.map +1 -0
  96. package/dist/components/cloud-layer.d.ts +29 -0
  97. package/dist/components/cloud-layer.d.ts.map +1 -0
  98. package/dist/components/index.d.ts +2 -0
  99. package/dist/components/index.d.ts.map +1 -1
  100. package/dist/components/instances.d.ts +20 -6
  101. package/dist/components/instances.d.ts.map +1 -1
  102. package/dist/construct-renderer.d.ts +1 -1
  103. package/dist/construct-renderer.d.ts.map +1 -1
  104. package/dist/construct-renderer.mjs +8406 -5785
  105. package/dist/construct-renderer.mjs.map +1 -1
  106. package/dist/device/gpu-residency.d.ts +19 -19
  107. package/dist/device/gpu-residency.d.ts.map +1 -1
  108. package/dist/dynamic-geometry.d.ts +5 -1
  109. package/dist/dynamic-geometry.d.ts.map +1 -1
  110. package/dist/errors/cloud.d.ts +59 -0
  111. package/dist/errors/cloud.d.ts.map +1 -0
  112. package/dist/errors/render.d.ts +20 -5
  113. package/dist/errors/render.d.ts.map +1 -1
  114. package/dist/examples/single-layer-medium-surface.d.ts +47 -0
  115. package/dist/examples/single-layer-medium-surface.d.ts.map +1 -0
  116. package/dist/extract/camera.d.ts.map +1 -1
  117. package/dist/extract/contracts.d.ts +5 -1
  118. package/dist/extract/contracts.d.ts.map +1 -1
  119. package/dist/extract/material-context.d.ts +7 -0
  120. package/dist/extract/material-context.d.ts.map +1 -0
  121. package/dist/features/barrel-distortion.d.ts +29 -0
  122. package/dist/features/barrel-distortion.d.ts.map +1 -0
  123. package/dist/features/error-reporting.d.ts +6 -2
  124. package/dist/features/error-reporting.d.ts.map +1 -1
  125. package/dist/features/host.d.ts +15 -0
  126. package/dist/features/host.d.ts.map +1 -1
  127. package/dist/features/plan.d.ts +10 -0
  128. package/dist/features/plan.d.ts.map +1 -1
  129. package/dist/features/render-graph-contribution.d.ts +5 -0
  130. package/dist/features/render-graph-contribution.d.ts.map +1 -1
  131. package/dist/features/render-graph-raster.d.ts +1 -0
  132. package/dist/features/render-graph-raster.d.ts.map +1 -1
  133. package/dist/features/targets.d.ts +2 -0
  134. package/dist/features/targets.d.ts.map +1 -1
  135. package/dist/features/types.d.ts +48 -0
  136. package/dist/features/types.d.ts.map +1 -1
  137. package/dist/fullscreen-post-process-pass.d.ts +15 -7
  138. package/dist/fullscreen-post-process-pass.d.ts.map +1 -1
  139. package/dist/glyph-text-layout-system.d.ts +1 -1
  140. package/dist/glyph-text-layout-system.d.ts.map +1 -1
  141. package/dist/gpu-dirty-ranges.d.ts +11 -0
  142. package/dist/gpu-dirty-ranges.d.ts.map +1 -0
  143. package/dist/gpu-driven/batch-topology.d.ts +17 -0
  144. package/dist/gpu-driven/batch-topology.d.ts.map +1 -1
  145. package/dist/gpu-driven/material-artifacts.d.ts +6 -0
  146. package/dist/gpu-driven/material-artifacts.d.ts.map +1 -1
  147. package/dist/gpu-driven/prepared-draw.d.ts.map +1 -1
  148. package/dist/gpu-driven/production-raster-material.d.ts +178 -0
  149. package/dist/gpu-driven/production-raster-material.d.ts.map +1 -0
  150. package/dist/gpu-driven/production-raster.d.ts +47 -3
  151. package/dist/gpu-driven/production-raster.d.ts.map +1 -1
  152. package/dist/gpu-driven/resource-allocation.d.ts +31 -0
  153. package/dist/gpu-driven/resource-allocation.d.ts.map +1 -0
  154. package/dist/gpu-driven/shadow-ownership.d.ts +14 -0
  155. package/dist/gpu-driven/shadow-ownership.d.ts.map +1 -0
  156. package/dist/gpu-driven/view-gpu.d.ts +26 -2
  157. package/dist/gpu-driven/view-gpu.d.ts.map +1 -1
  158. package/dist/gpu-scene.d.ts +5 -0
  159. package/dist/gpu-scene.d.ts.map +1 -1
  160. package/dist/hdrp-buffers.d.ts +2 -2
  161. package/dist/hdrp-buffers.d.ts.map +1 -1
  162. package/dist/ibl/frame-resources.d.ts.map +1 -1
  163. package/dist/ibl/skylight-bind-group.d.ts +14 -5
  164. package/dist/ibl/skylight-bind-group.d.ts.map +1 -1
  165. package/dist/index.d.ts +30 -7
  166. package/dist/index.d.ts.map +1 -1
  167. package/dist/index.mjs +780 -9
  168. package/dist/index.mjs.map +1 -1
  169. package/dist/inspection-types.d.ts +135 -1
  170. package/dist/inspection-types.d.ts.map +1 -1
  171. package/dist/instance-buffer-cache.d.ts +4 -4
  172. package/dist/instance-buffer-cache.d.ts.map +1 -1
  173. package/dist/instances.d.ts +47 -100
  174. package/dist/instances.d.ts.map +1 -1
  175. package/dist/internal.mjs +7 -8
  176. package/dist/internal.mjs.map +1 -1
  177. package/dist/pbr-pipeline.d.ts +22 -4
  178. package/dist/pbr-pipeline.d.ts.map +1 -1
  179. package/dist/pipeline/single-layer-medium-passes.d.ts +73 -0
  180. package/dist/pipeline/single-layer-medium-passes.d.ts.map +1 -0
  181. package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
  182. package/dist/pipeline/standard-output/graph.d.ts.map +1 -1
  183. package/dist/pipeline/standard-output/lut-gpu.d.ts +2 -2
  184. package/dist/pipeline/standard-output/lut-gpu.d.ts.map +1 -1
  185. package/dist/pipeline/standard-output/resources.d.ts +2 -2
  186. package/dist/pipeline/standard-output/resources.d.ts.map +1 -1
  187. package/dist/pipeline/standard-output/types.d.ts +4 -2
  188. package/dist/pipeline/standard-output/types.d.ts.map +1 -1
  189. package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
  190. package/dist/pipeline/standard-post.d.ts.map +1 -1
  191. package/dist/pipeline-builder.d.ts +1 -1
  192. package/dist/pipeline-builder.d.ts.map +1 -1
  193. package/dist/plugin.d.ts.map +1 -1
  194. package/dist/prepare/prepared-graphics-resolver.d.ts +2 -0
  195. package/dist/prepare/prepared-graphics-resolver.d.ts.map +1 -1
  196. package/dist/publication/contract.d.ts +77 -0
  197. package/dist/publication/contract.d.ts.map +1 -0
  198. package/dist/publication/dependencies.d.ts +7 -0
  199. package/dist/publication/dependencies.d.ts.map +1 -0
  200. package/dist/publication/prepare-geometry.d.ts +9 -0
  201. package/dist/publication/prepare-geometry.d.ts.map +1 -0
  202. package/dist/publication/programs.d.ts +5 -0
  203. package/dist/publication/programs.d.ts.map +1 -0
  204. package/dist/publication/publisher.d.ts +33 -0
  205. package/dist/publication/publisher.d.ts.map +1 -0
  206. package/dist/publication/receiver.d.ts +29 -0
  207. package/dist/publication/receiver.d.ts.map +1 -0
  208. package/dist/publication/resource-scope.d.ts +18 -0
  209. package/dist/publication/resource-scope.d.ts.map +1 -0
  210. package/dist/publication/targets.d.ts +33 -0
  211. package/dist/publication/targets.d.ts.map +1 -0
  212. package/dist/publication/video.d.ts +5 -0
  213. package/dist/publication/video.d.ts.map +1 -0
  214. package/dist/record/barrel-distortion-frame.d.ts +15 -0
  215. package/dist/record/barrel-distortion-frame.d.ts.map +1 -0
  216. package/dist/record/dynamic-geometry-consumption.d.ts +10 -2
  217. package/dist/record/dynamic-geometry-consumption.d.ts.map +1 -1
  218. package/dist/record/fold-buckets.d.ts +95 -0
  219. package/dist/record/fold-buckets.d.ts.map +1 -0
  220. package/dist/record/fold-instance-buffer.d.ts +6 -0
  221. package/dist/record/fold-instance-buffer.d.ts.map +1 -0
  222. package/dist/record/frame-lighting.d.ts +1 -0
  223. package/dist/record/frame-lighting.d.ts.map +1 -1
  224. package/dist/record/frame-snapshot.d.ts +43 -9
  225. package/dist/record/frame-snapshot.d.ts.map +1 -1
  226. package/dist/record/frame.d.ts +15 -65
  227. package/dist/record/frame.d.ts.map +1 -1
  228. package/dist/record/gpu-draw-receipts.d.ts +11 -0
  229. package/dist/record/gpu-draw-receipts.d.ts.map +1 -0
  230. package/dist/record/gpu-timing.d.ts +8 -0
  231. package/dist/record/gpu-timing.d.ts.map +1 -1
  232. package/dist/record/helpers.d.ts +2 -2
  233. package/dist/record/helpers.d.ts.map +1 -1
  234. package/dist/record/main-pass-geometry.d.ts +22 -6
  235. package/dist/record/main-pass-geometry.d.ts.map +1 -1
  236. package/dist/record/main-pass-material.d.ts +14 -10
  237. package/dist/record/main-pass-material.d.ts.map +1 -1
  238. package/dist/record/main-pass-sprite-draws.d.ts +3 -16
  239. package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
  240. package/dist/record/main-pass.d.ts +13 -2
  241. package/dist/record/main-pass.d.ts.map +1 -1
  242. package/dist/record/motion-blur-receipt.d.ts +11 -0
  243. package/dist/record/motion-blur-receipt.d.ts.map +1 -0
  244. package/dist/record/prepared-material-bindings.d.ts +3 -3
  245. package/dist/record/prepared-material-bindings.d.ts.map +1 -1
  246. package/dist/record/probe-blend-buffer.d.ts +16 -0
  247. package/dist/record/probe-blend-buffer.d.ts.map +1 -0
  248. package/dist/record/recovery-pipeline.d.ts.map +1 -1
  249. package/dist/record/render-context.d.ts +24 -5
  250. package/dist/record/render-context.d.ts.map +1 -1
  251. package/dist/record/shadow-pass.d.ts +2 -0
  252. package/dist/record/shadow-pass.d.ts.map +1 -1
  253. package/dist/record/skybox-post-pass.d.ts +1 -1
  254. package/dist/record/skybox-post-pass.d.ts.map +1 -1
  255. package/dist/record/sprite-instance-buffer.d.ts +31 -0
  256. package/dist/record/sprite-instance-buffer.d.ts.map +1 -0
  257. package/dist/record/target-capture-graph.d.ts +19 -0
  258. package/dist/record/target-capture-graph.d.ts.map +1 -0
  259. package/dist/record/typed-frame-graph.d.ts +24 -15
  260. package/dist/record/typed-frame-graph.d.ts.map +1 -1
  261. package/dist/record/view-ubo.d.ts +11 -3
  262. package/dist/record/view-ubo.d.ts.map +1 -1
  263. package/dist/record/volume-params.d.ts +9 -0
  264. package/dist/record/volume-params.d.ts.map +1 -0
  265. package/dist/recovery/render-system-candidate.d.ts +14 -4
  266. package/dist/recovery/render-system-candidate.d.ts.map +1 -1
  267. package/dist/reflection/record-owner.d.ts.map +1 -1
  268. package/dist/render-contract.d.ts +46 -5
  269. package/dist/render-contract.d.ts.map +1 -1
  270. package/dist/render-graph-primitives.d.ts.map +1 -1
  271. package/dist/render-pipeline.d.ts +103 -2
  272. package/dist/render-pipeline.d.ts.map +1 -1
  273. package/dist/render-system-extract-tail.d.ts +3 -2
  274. package/dist/render-system-extract-tail.d.ts.map +1 -1
  275. package/dist/render-system-extract.d.ts +18 -9
  276. package/dist/render-system-extract.d.ts.map +1 -1
  277. package/dist/render-system-presentation.d.ts +23 -0
  278. package/dist/render-system-presentation.d.ts.map +1 -0
  279. package/dist/render-system-producer-roots.d.ts +10 -0
  280. package/dist/render-system-producer-roots.d.ts.map +1 -0
  281. package/dist/render-system-projections.d.ts +3 -2
  282. package/dist/render-system-projections.d.ts.map +1 -1
  283. package/dist/render-system-residency.d.ts +12 -0
  284. package/dist/render-system-residency.d.ts.map +1 -0
  285. package/dist/render-system.d.ts +22 -21
  286. package/dist/render-system.d.ts.map +1 -1
  287. package/dist/scene/probe-blend.d.ts +27 -0
  288. package/dist/scene/probe-blend.d.ts.map +1 -1
  289. package/dist/scene/render-scene.d.ts +18 -7
  290. package/dist/scene/render-scene.d.ts.map +1 -1
  291. package/dist/scene/render-source.d.ts +42 -0
  292. package/dist/scene/render-source.d.ts.map +1 -0
  293. package/dist/scene/source-systems.d.ts +6 -0
  294. package/dist/scene/source-systems.d.ts.map +1 -0
  295. package/dist/ssao-buffers.d.ts +9 -0
  296. package/dist/ssao-buffers.d.ts.map +1 -1
  297. package/dist/surface/dynamic-input.d.ts +133 -0
  298. package/dist/surface/dynamic-input.d.ts.map +1 -0
  299. package/dist/surface/gpu-driven.d.ts +41 -0
  300. package/dist/surface/gpu-driven.d.ts.map +1 -0
  301. package/dist/surface/submission-observation.d.ts +136 -0
  302. package/dist/surface/submission-observation.d.ts.map +1 -0
  303. package/dist/systems/skin-palette-allocator.d.ts.map +1 -1
  304. package/dist/systems/skin-palette-types.d.ts +10 -0
  305. package/dist/systems/skin-palette-types.d.ts.map +1 -1
  306. package/dist/systems/transparent-dispatch.d.ts +2 -2
  307. package/dist/systems/transparent-dispatch.d.ts.map +1 -1
  308. package/dist/systems/transparent-sort-config.d.ts +2 -1
  309. package/dist/systems/transparent-sort-config.d.ts.map +1 -1
  310. package/dist/temporal/gpu.d.ts +13 -1
  311. package/dist/temporal/gpu.d.ts.map +1 -1
  312. package/dist/temporal/index.mjs +6 -7
  313. package/dist/temporal/inspection.d.ts.map +1 -1
  314. package/dist/temporal/standard-scene-data.d.ts.map +1 -1
  315. package/dist/temporal/target-coverage-attachment.d.ts.map +1 -1
  316. package/dist/transmission/single-layer-medium.d.ts +89 -0
  317. package/dist/transmission/single-layer-medium.d.ts.map +1 -0
  318. package/dist/typed-render-graph-primitives.d.ts +14 -2
  319. package/dist/typed-render-graph-primitives.d.ts.map +1 -1
  320. package/dist/volume/component.d.ts +9 -4
  321. package/dist/volume/component.d.ts.map +1 -1
  322. package/dist/volume/extract.d.ts +1 -1
  323. package/dist/volume/extract.d.ts.map +1 -1
  324. package/dist/volume/inspection.d.ts +2 -0
  325. package/dist/volume/inspection.d.ts.map +1 -1
  326. package/dist/volume/passes.d.ts +4 -1
  327. package/dist/volume/passes.d.ts.map +1 -1
  328. package/dist/volume/resources.d.ts.map +1 -1
  329. package/package.json +22 -21
  330. package/src/__tests__/atmosphere-daylight.dawn.test.ts +72 -0
  331. package/src/__tests__/auto-exposure-transaction.integration.test.ts +4 -9
  332. package/src/__tests__/barrel-distortion-lkg.integration.test.ts +353 -0
  333. package/src/__tests__/barrel-distortion.dawn.test.ts +41 -0
  334. package/src/__tests__/barrel-distortion.unit.test.ts +364 -0
  335. package/src/__tests__/construct-renderer-identity.unit.test.ts +49 -0
  336. package/src/__tests__/detached-recovery-graph.unit.test.ts +277 -0
  337. package/src/__tests__/device-feature-admission.unit.test.ts +3 -2
  338. package/src/__tests__/device-loss-fanout.unit.test.ts +36 -1
  339. package/src/__tests__/docs-gate-green.unit.test.ts +112 -1
  340. package/src/__tests__/factory-contract.integration.test.ts +295 -45
  341. package/src/__tests__/feature-depth-input.dawn.test.ts +129 -2
  342. package/src/__tests__/fullscreen-feature-plan.unit.test.ts +14 -3
  343. package/src/__tests__/gpu-dirty-ranges.unit.test.ts +22 -0
  344. package/src/__tests__/gpu-driven-baseline.characterization.test.ts +16 -3
  345. package/src/__tests__/gpu-driven-batch-topology-pbr.unit.test.ts +12 -0
  346. package/src/__tests__/gpu-driven-material-artifacts.unit.test.ts +143 -0
  347. package/src/__tests__/gpu-driven-pbr.dawn.test.ts +5 -6
  348. package/src/__tests__/gpu-driven-production.integration.test.ts +482 -3
  349. package/src/__tests__/gpu-driven-shadow-views.dawn.test.ts +6 -5
  350. package/src/__tests__/gpu-driven-view-gpu-evidence.ts +94 -6
  351. package/src/__tests__/gpu-driven-view-graph.integration.test.ts +18 -2
  352. package/src/__tests__/gpu-driven-view.browser.test.ts +5 -8
  353. package/src/__tests__/gpu-driven-view.dawn.test.ts +6 -2
  354. package/src/__tests__/gpu-driven-view.unit.test.ts +1 -1
  355. package/src/__tests__/gpu-pass-timing-lifecycle.integration.test.ts +1 -0
  356. package/src/__tests__/gpu-pass-timing.browser.test.ts +6 -4
  357. package/src/__tests__/gpu-pass-timing.rhi-null.unit.test.ts +1 -0
  358. package/src/__tests__/gpu-scene.unit.test.ts +119 -9
  359. package/src/__tests__/instance-allocation-failure.unit.test.ts +57 -0
  360. package/src/__tests__/instances-culling.integration.test.ts +10 -36
  361. package/src/__tests__/instances-store.unit.test.ts +97 -305
  362. package/src/__tests__/instances-world-ownership.integration.test.ts +62 -0
  363. package/src/__tests__/ktx2-basis-gpu-consumer.dawn.test.ts +4 -1
  364. package/src/__tests__/main-pass-material-medium-selection.unit.test.ts +145 -0
  365. package/src/__tests__/material-shader-prewarm.unit.test.ts +59 -0
  366. package/src/__tests__/material-snapshot-mutation.integration.test.ts +39 -1
  367. package/src/__tests__/materials-standard-contract.unit.test.ts +0 -1
  368. package/src/__tests__/morph-culling-reentry.dawn.test.ts +5 -5
  369. package/src/__tests__/morph-visual-evidence.integration.test.ts +8 -7
  370. package/src/__tests__/motion-blur-compute.dawn.test.ts +13 -7
  371. package/src/__tests__/motion-blur.pixel.integration.test.ts +2 -2
  372. package/src/__tests__/nineslice-handle-surface.unit.test.ts +4 -4
  373. package/src/__tests__/pbr-view-layout.unit.test.ts +9 -0
  374. package/src/__tests__/physical-clearcoat.integration.test.ts +66 -8
  375. package/src/__tests__/physical-material-budget.unit.test.ts +40 -0
  376. package/src/__tests__/prepared-graphics-capability.unit.test.ts +66 -2
  377. package/src/__tests__/probe-bindings.unit.test.ts +582 -1
  378. package/src/__tests__/probe-scene-dirty.integration.test.ts +28 -1
  379. package/src/__tests__/receipt-observation-retirement.unit.test.ts +65 -0
  380. package/src/__tests__/recovery-candidate-prepare.contract.test.ts +69 -0
  381. package/src/__tests__/render-error-code-owner.test-d.ts +8 -0
  382. package/src/__tests__/render-feature-diagnostics.unit.test.ts +15 -2
  383. package/src/__tests__/render-feature-error-reporting.unit.test.ts +21 -1
  384. package/src/__tests__/render-feature-prepared-diagnostics.unit.test.ts +10 -1
  385. package/src/__tests__/render-feature-retry.unit.test.ts +12 -1
  386. package/src/__tests__/render-scene-mixed-updates.integration.test.ts +145 -19
  387. package/src/__tests__/render-scene-projection.unit.test.ts +36 -11
  388. package/src/__tests__/render-scene-temporal-retry.unit.test.ts +20 -20
  389. package/src/__tests__/render-target-probe-soak.perf.test.ts +2 -2
  390. package/src/__tests__/render-temporal-environment-consumer.browser.test.ts +3 -3
  391. package/src/__tests__/render-temporal-environment-consumer.dawn.test.ts +5 -2
  392. package/src/__tests__/renderer-factory-material-contract.unit.test.ts +42 -8
  393. package/src/__tests__/renderer-frame-generation-fence.integration.test.ts +16 -0
  394. package/src/__tests__/renderer-frame-transaction.integration.test.ts +26 -10
  395. package/src/__tests__/resource-allocation.unit.test.ts +123 -0
  396. package/src/__tests__/single-layer-medium-passes.unit.test.ts +275 -0
  397. package/src/__tests__/skin-motion-regression.unit.test.ts +70 -1
  398. package/src/__tests__/skin-palette-recycling.dawn.test.ts +97 -0
  399. package/src/__tests__/skinned-shadow-caster.test.ts +15 -0
  400. package/src/__tests__/ssr-carrier-public-contract.unit.test.ts +21 -0
  401. package/src/__tests__/ssr-gpu-dispatch.browser.test.ts +4 -4
  402. package/src/__tests__/ssr-gpu-dispatch.dawn.test.ts +2 -2
  403. package/src/__tests__/ssr-gpu-dispatch.ts +1 -1
  404. package/src/__tests__/ssr-receiver-coverage.dawn.test.ts +6 -2
  405. package/src/__tests__/standard-output-chain-contract.unit.test.ts +28 -0
  406. package/src/__tests__/standard-pbr-artifact-assembly.unit.test.ts +46 -1
  407. package/src/__tests__/standard-pipeline.integration.test.ts +182 -1
  408. package/src/__tests__/surface-dynamic-buffer-lifecycle.unit.test.ts +506 -0
  409. package/src/__tests__/surface-submission-observation.unit.test.ts +812 -0
  410. package/src/__tests__/temporal-history-lifecycle.unit.test.ts +32 -0
  411. package/src/__tests__/temporal-performance.integration.test.ts +3 -3
  412. package/src/__tests__/visibility-instances.integration.test.ts +7 -19
  413. package/src/__tests__/visibility-producer-matrix.integration.test.ts +9 -20
  414. package/src/__tests__/volume-optics-oracle.unit.test.ts +4 -2
  415. package/src/__tests__/volumetric-fog-budget-recovery.integration.test.ts +30 -20
  416. package/src/__tests__/volumetric-fog-errors.unit.test.ts +4 -3
  417. package/src/__tests__/volumetric-fog-multiple.unit.test.ts +55 -0
  418. package/src/__tests__/volumetric-fog-spot-frame-optics.integration.test.ts +1 -1
  419. package/src/__tests__/volumetric-fog-stage-readback.dawn.test.ts +113 -9
  420. package/src/__tests__/volumetric-fog-topology.unit.test.ts +5 -3
  421. package/src/assembly/backend-contract.ts +30 -0
  422. package/src/assembly/barrel-distortion-inspection.ts +22 -0
  423. package/src/assembly/canvas-draw-guard.ts +11 -0
  424. package/src/assembly/cloud-shadow-entry.ts +22 -0
  425. package/src/assembly/device-feature-admission.ts +2 -2
  426. package/src/assembly/dynamic-geometry-host.ts +65 -37
  427. package/src/assembly/dynamic-geometry-runtime.ts +7 -1
  428. package/src/assembly/factory.ts +2 -30
  429. package/src/assembly/host-contract.ts +8 -5
  430. package/src/assembly/material/assembly.ts +25 -13
  431. package/src/assembly/material/pipeline-helpers.ts +53 -12
  432. package/src/assembly/material-shader-policy.ts +55 -0
  433. package/src/assembly/material-shader-prewarm.ts +21 -10
  434. package/src/assembly/recovery/device-loss-fanout.ts +13 -0
  435. package/src/assembly/recovery/renderer-recover.ts +50 -18
  436. package/src/assembly/render-feature-post-processes.ts +54 -0
  437. package/src/assembly/render-target-host.ts +57 -47
  438. package/src/assembly/renderer-facade.ts +7 -2
  439. package/src/assembly/renderer-feature-inspection.ts +8 -2
  440. package/src/assembly/renderer-frame-transaction.ts +23 -0
  441. package/src/assembly/renderer-inspection.ts +28 -0
  442. package/src/assembly/skin-palette-owner.ts +1 -1
  443. package/src/assembly/temporal-post-process-prewarm.ts +3 -3
  444. package/src/assembly/webgpu-pbr-ready.ts +9 -1
  445. package/src/assembly/webgpu-ready-per-pass.ts +104 -0
  446. package/src/assembly/webgpu-ready.ts +116 -62
  447. package/src/assembly/webgpu-renderer-bootstrap.ts +0 -12
  448. package/src/assembly/webgpu-renderer-observation-owner.ts +222 -0
  449. package/src/assembly/webgpu-renderer.ts +303 -313
  450. package/src/assembly/webgpu-vertex-layouts.ts +1 -1
  451. package/src/barrel-distortion.ts +247 -0
  452. package/src/cloud/__tests__/cloud-density-parity.dawn.test.ts +173 -0
  453. package/src/cloud/__tests__/cloud-layer-framebuffer-y.dawn.test.ts +265 -0
  454. package/src/cloud/__tests__/cloud-layer.browser.test.ts +432 -0
  455. package/src/cloud/__tests__/cloud-layer.unit.test.ts +634 -0
  456. package/src/cloud/density.ts +600 -0
  457. package/src/cloud/extract.ts +110 -0
  458. package/src/cloud/feature.ts +1810 -0
  459. package/src/cloud/frame-context.ts +42 -0
  460. package/src/cloud/inspection.ts +130 -0
  461. package/src/cloud/optics.ts +285 -0
  462. package/src/cloud/parameters.ts +255 -0
  463. package/src/cloud/resources.ts +122 -0
  464. package/src/cloud/shadow.ts +167 -0
  465. package/src/cloud/temporal.ts +226 -0
  466. package/src/components/barrel-distortion.ts +46 -0
  467. package/src/components/cloud-layer.ts +47 -0
  468. package/src/components/index.ts +2 -0
  469. package/src/components/instances.ts +18 -45
  470. package/src/components/sprite-instances.ts +1 -1
  471. package/src/construct-renderer.ts +1 -1
  472. package/src/device/gpu-residency.ts +26 -25
  473. package/src/dynamic-geometry.ts +12 -1
  474. package/src/errors/cloud.ts +103 -0
  475. package/src/errors/render.ts +47 -8
  476. package/src/examples/single-layer-medium-surface.ts +59 -0
  477. package/src/extract/camera.ts +15 -1
  478. package/src/extract/contracts.ts +5 -0
  479. package/src/extract/material-context.ts +21 -0
  480. package/src/features/barrel-distortion.ts +172 -0
  481. package/src/features/error-reporting.ts +26 -6
  482. package/src/features/host.ts +128 -19
  483. package/src/features/plan.ts +10 -0
  484. package/src/features/render-graph-contribution.ts +9 -0
  485. package/src/features/render-graph-raster.ts +45 -3
  486. package/src/features/targets.ts +66 -0
  487. package/src/features/types.ts +51 -0
  488. package/src/fullscreen-post-process-pass.ts +41 -9
  489. package/src/glyph-text-layout-system.ts +2 -2
  490. package/src/gpu-dirty-ranges.ts +48 -0
  491. package/src/gpu-driven/batch-topology.ts +53 -0
  492. package/src/gpu-driven/material-artifacts.ts +33 -25
  493. package/src/gpu-driven/prepared-draw.ts +12 -4
  494. package/src/gpu-driven/production-raster-material.ts +1894 -0
  495. package/src/gpu-driven/production-raster.ts +825 -784
  496. package/src/gpu-driven/resource-allocation.ts +142 -0
  497. package/src/gpu-driven/shadow-ownership.ts +71 -0
  498. package/src/gpu-driven/view-gpu.ts +164 -10
  499. package/src/gpu-scene.ts +75 -84
  500. package/src/hdrp-buffers.ts +4 -3
  501. package/src/ibl/frame-resources.ts +0 -1
  502. package/src/ibl/skylight-bind-group.ts +62 -32
  503. package/src/index.ts +205 -9
  504. package/src/inspection-types.ts +162 -0
  505. package/src/instance-buffer-cache.ts +4 -4
  506. package/src/instances.ts +152 -569
  507. package/src/pbr-pipeline.ts +230 -30
  508. package/src/pipeline/single-layer-medium-passes.ts +267 -0
  509. package/src/pipeline/standard-forward-lane.ts +313 -57
  510. package/src/pipeline/standard-output/graph.ts +8 -2
  511. package/src/pipeline/standard-output/lut-gpu.ts +3 -3
  512. package/src/pipeline/standard-output/resources.ts +2 -2
  513. package/src/pipeline/standard-output/types.ts +12 -1
  514. package/src/pipeline/standard-pipeline.ts +291 -18
  515. package/src/pipeline/standard-post.ts +90 -24
  516. package/src/pipeline-builder.ts +1 -1
  517. package/src/plugin.ts +4 -0
  518. package/src/points-lines/__tests__/points-lines.bench.ts +17 -8
  519. package/src/prepare/prepared-graphics-resolver.ts +2 -0
  520. package/src/publication/__tests__/derived-geometry.integration.test.ts +177 -0
  521. package/src/publication/__tests__/features.integration.test.ts +122 -0
  522. package/src/publication/__tests__/programs.unit.test.ts +25 -0
  523. package/src/publication/__tests__/publication.integration.test.ts +370 -0
  524. package/src/publication/__tests__/targets.integration.test.ts +120 -0
  525. package/src/publication/__tests__/video.browser.test.ts +50 -0
  526. package/src/publication/contract.ts +104 -0
  527. package/src/publication/dependencies.ts +54 -0
  528. package/src/publication/prepare-geometry.ts +94 -0
  529. package/src/publication/programs.ts +45 -0
  530. package/src/publication/publisher.ts +618 -0
  531. package/src/publication/receiver.ts +297 -0
  532. package/src/publication/resource-scope.ts +31 -0
  533. package/src/publication/targets.ts +182 -0
  534. package/src/publication/video.ts +33 -0
  535. package/src/record/__tests__/main-pass-geometry.unit.test.ts +30 -0
  536. package/src/record/__tests__/material-bg-cache.unit.test.ts +15 -1
  537. package/src/record/__tests__/sprite-material-abi.unit.test.ts +9 -11
  538. package/src/record/__tests__/temporal-payload.unit.test.ts +1 -1
  539. package/src/record/__tests__/transparent-group2-selection.unit.test.ts +11 -0
  540. package/src/record/__tests__/typed-frame-graph-plan.unit.test.ts +1 -0
  541. package/src/record/__tests__/video-texture-view.unit.test.ts +9 -9
  542. package/src/record/__tests__/view-ubo-layout.unit.test.ts +4 -3
  543. package/src/record/barrel-distortion-frame.ts +110 -0
  544. package/src/record/dynamic-geometry-consumption.ts +39 -9
  545. package/src/record/fold-buckets.ts +855 -0
  546. package/src/record/fold-instance-buffer.ts +51 -0
  547. package/src/record/frame-lighting.ts +36 -2
  548. package/src/record/frame-snapshot.ts +45 -9
  549. package/src/record/frame.ts +258 -1105
  550. package/src/record/gpu-draw-receipts.ts +38 -0
  551. package/src/record/gpu-timing.ts +48 -5
  552. package/src/record/helpers.ts +2 -2
  553. package/src/record/main-pass-geometry.ts +272 -84
  554. package/src/record/main-pass-material.ts +102 -36
  555. package/src/record/main-pass-sprite-draws.ts +13 -217
  556. package/src/record/main-pass.ts +164 -27
  557. package/src/record/motion-blur-receipt.ts +91 -0
  558. package/src/record/prepared-material-bindings.ts +11 -6
  559. package/src/record/probe-blend-buffer.ts +180 -0
  560. package/src/record/recovery-pipeline.ts +35 -3
  561. package/src/record/render-context.ts +41 -5
  562. package/src/record/shadow-pass.ts +30 -4
  563. package/src/record/skybox-post-pass.ts +28 -4
  564. package/src/record/sprite-instance-buffer.ts +155 -0
  565. package/src/record/target-capture-graph.ts +258 -0
  566. package/src/record/typed-frame-graph.ts +776 -340
  567. package/src/record/view-ubo.ts +27 -4
  568. package/src/record/volume-params.ts +225 -0
  569. package/src/recovery/render-system-candidate.ts +243 -50
  570. package/src/reflection/record-owner.ts +7 -3
  571. package/src/render-contract.ts +54 -5
  572. package/src/render-graph-primitives.ts +7 -1
  573. package/src/render-pipeline.ts +130 -0
  574. package/src/render-system-extract-tail.ts +90 -78
  575. package/src/render-system-extract.ts +76 -76
  576. package/src/render-system-presentation.ts +56 -0
  577. package/src/render-system-producer-roots.ts +96 -0
  578. package/src/render-system-projections.ts +3 -2
  579. package/src/render-system-residency.ts +159 -0
  580. package/src/render-system.ts +421 -408
  581. package/src/scene/probe-blend.ts +96 -4
  582. package/src/scene/render-scene.ts +416 -337
  583. package/src/scene/render-source.ts +90 -0
  584. package/src/scene/source-systems.ts +33 -0
  585. package/src/ssao-buffers.ts +95 -2
  586. package/src/surface/__tests__/dynamic-input.unit.test.ts +374 -0
  587. package/src/surface/__tests__/gpu-driven.unit.test.ts +87 -0
  588. package/src/surface/dynamic-input.ts +658 -0
  589. package/src/surface/gpu-driven.ts +147 -0
  590. package/src/surface/submission-observation.ts +517 -0
  591. package/src/systems/skin-palette-allocator.ts +5 -1
  592. package/src/systems/skin-palette-types.ts +11 -0
  593. package/src/systems/transparent-dispatch.ts +2 -2
  594. package/src/systems/transparent-sort-config.ts +3 -1
  595. package/src/temporal/gpu.ts +101 -17
  596. package/src/temporal/inspection.ts +7 -1
  597. package/src/temporal/standard-scene-data.ts +5 -0
  598. package/src/temporal/target-coverage-attachment.ts +4 -0
  599. package/src/transmission/__tests__/single-layer-medium.unit.test.ts +149 -0
  600. package/src/transmission/__tests__/standard-transmission.dawn.test.ts +12 -11
  601. package/src/transmission/single-layer-medium.ts +322 -0
  602. package/src/typed-render-graph-primitives.ts +70 -2
  603. package/src/volume/component.ts +10 -9
  604. package/src/volume/extract.ts +11 -5
  605. package/src/volume/inspection.ts +3 -0
  606. package/src/volume/passes.ts +87 -4
  607. package/src/volume/resources.ts +2 -1
  608. package/dist/chunk-3RTCJTH4.mjs +0 -1610
  609. package/dist/chunk-3RTCJTH4.mjs.map +0 -1
  610. package/dist/chunk-BLXGONR2.mjs.map +0 -1
  611. package/dist/chunk-ILQ5MEZF.mjs +0 -32
  612. package/dist/chunk-ILQ5MEZF.mjs.map +0 -1
  613. package/dist/chunk-LEAXOMYJ.mjs.map +0 -1
  614. package/dist/chunk-NB2S7ZRX.mjs +0 -1783
  615. package/dist/chunk-NB2S7ZRX.mjs.map +0 -1
  616. package/dist/chunk-NNQL7CB3.mjs +0 -1713
  617. package/dist/chunk-NNQL7CB3.mjs.map +0 -1
  618. package/dist/chunk-OO3DRYK7.mjs.map +0 -1
  619. package/dist/chunk-PA5DRJNI.mjs.map +0 -1
  620. package/dist/chunk-PLAXOOX5.mjs +0 -1907
  621. package/dist/chunk-PLAXOOX5.mjs.map +0 -1
  622. package/dist/chunk-RXZ3RGOU.mjs +0 -53
  623. package/dist/chunk-RXZ3RGOU.mjs.map +0 -1
  624. package/dist/chunk-VOZWDM3L.mjs.map +0 -1
@@ -1,1246 +1,2394 @@
1
- import { GlyphText } from './chunk-ILQ5MEZF.mjs';
2
- import { MeshRenderer, MeshFilter, DirectionalLight, validateDirectionalLightData, PointLight, computeInvRangeSquared, SpotLight, degToCos, RectAreaLight, validateRectAreaLightData, directionalShadowQualityFromF32, PointLightShadow, Skylight, LightProbe, validateLightProbeData, SkyboxBackground, Atmosphere, selectEnvironment, Instances, Layer, Points, Lines, SortKey, resolveMeshMaterialBindings, pointShapeFromU32, PostProcessParams, buildCubeCameraFaceViews, CubeCamera, cubeCameraUpdateIntentFromF32, ReflectionProbe, reflectionProbeUpdateIntentFromF32, tonemapToU32, resolveVisibility, Fog, VolumetricFog, resolveSelectedVolumetricLight, extractVolumetricFog, MotionBlur, DynamicResolution, ScreenSpaceReflection, DepthOfField, Camera, cameraProjectionFromF32, resolveDepthOfFieldParams, depthOfFieldRequestFailure, CAMERA_EXPOSURE_MODE_MANUAL, cameraExposureFromColumns, antialiasFromF32, validateDynamicResolutionCamera, validateCameraBloom, tonemapFromF32 } from './chunk-PLAXOOX5.mjs';
3
- import { SpriteRegionOverride, SpriteInstances, selectActiveCameraIndex, getActiveCamera } from './chunk-PA5DRJNI.mjs';
4
- import { PROBE_BLEND_RECORD_CAPACITY, GPU_BUFFER_USAGE_MAP_READ, GPU_BUFFER_USAGE_VERTEX, GPU_BUFFER_USAGE_COPY_DST, gpuDrivenShadowDrawKey, IES_SLICE_CAPACITY, COOKIE_SLICE_CAPACITY, isStandardPbrSkinMaterialShader, buildGpuDrivenDraws, worldEntityKey, gpuDrivenSourceDrawItemIndex, selectPasses, resolveRenderTargetMaterialSource, isCanonicalStandardPbrMaterialShader, PROBE_BLEND_RECORD_BYTE_SIZE, GPU_BUFFER_USAGE_UNIFORM, GPU_BUFFER_USAGE_QUERY_RESOLVE, GPU_BUFFER_USAGE_COPY_SRC, COOKIE_SLICE_SIZE, isStandardPbrMaterialShader } from './chunk-3RTCJTH4.mjs';
5
- import { MotionBlurValidationError, STANDARD_OUTPUT_TRANSFORM_FEATURE_ID, SHADOW_ATLAS_DEFAULT_LAYERS, MaterialSkinAttrMissingError, SkinMaterialMismatchError, InstanceCollectionError, ShadowInvalidConfigError, resolveMotionBlurParams, GPU_TEXTURE_USAGE_COPY_SRC, GPU_TEXTURE_USAGE_COPY_DST, GPU_TEXTURE_USAGE_TEXTURE_BINDING, GPU_TEXTURE_USAGE_STORAGE_BINDING, GPU_TEXTURE_USAGE_RENDER_ATTACHMENT } from './chunk-NB2S7ZRX.mjs';
6
- import { R_MIN, ok, err, TextError, toShared, handleSlot, AssetError, ASSET_ERROR_HINTS, materialValuesToLinearRuntime, derive, unpackSlot, materialGuidText } from '@forgeax/engine-types';
7
- import { err as err$1, ok as ok$1, RhiError } from '@forgeax/engine-rhi';
8
- import { resolveAssetHandle, walkMaterialPassesOverSharedRefs, selectMaterialPassProgram, materialParametersToParamSchema as materialParametersToParamSchema$1 } from '@forgeax/engine-assets-runtime';
9
- import { Time } from '@forgeax/engine-ecs';
1
+ import { GlyphText, SpriteRegionOverride, SpriteInstances, selectActiveCameraIndex, getActiveCamera } from './chunk-X2KA6WHM.mjs';
2
+ import { RenderIntentInvalidError, DynamicResolutionRequiresTaaError, ShadowInvalidConfigError, VolumeDensityShapeMismatchError, VolumeInvalidBoundsError, VolumeInvalidParametersError, VolumeOwnerConflictError, MotionBlurValidationError, gpuDrivenShadowDrawKey, STANDARD_OUTPUT_TRANSFORM_FEATURE_ID, IES_SLICE_CAPACITY, COOKIE_SLICE_CAPACITY, SHADOW_ATLAS_DEFAULT_LAYERS, MaterialSkinAttrMissingError, isStandardPbrSkinMaterialShader, SkinMaterialMismatchError, validateInstanceTransforms, buildGpuDrivenDraws, worldEntityKey, gpuDrivenSourceDrawItemIndex, selectPasses, resolveRenderTargetMaterialSource, isCanonicalStandardPbrMaterialShader, BarrelDistortionInvalidParameterError, DynamicResolutionInvalidParameterError, COOKIE_SLICE_SIZE, EnvironmentSourceConflictError, FogCardinalityError, resolveMotionBlurParams, isStandardPbrMaterialShader, AtmosphereInvalidParameterError } from './chunk-MXTWKZO4.mjs';
3
+ import { defineComponent, Entity, Time } from '@forgeax/engine-ecs';
4
+ import { R_MIN, err, ok, TextError, toShared, handleSlot, AssetError, ASSET_ERROR_HINTS, materialValuesToLinearRuntime, derive, unpackSlot, materialGuidText } from '@forgeax/engine-types';
5
+ import { TONEMAP_SHADER_MODE, standardTextureMask, DEFAULT_STANDARD_PBR_PARAM_SCHEMA, STANDARD_PIPELINE_PARAM_SCHEMA, DEFAULT_STANDARD_SURFACE_MODULE } from '@forgeax/engine-shader';
10
6
  import { SpawnLightInvalidBoundsError, routeWorldError, SpriteInstancesMutuallyExclusiveWithInstancesError, SpriteInstancesRequiresSpriteShaderError, SpriteInstancesCountMismatchError, readRenderArrayView } from '@forgeax/engine-ecs/projection';
7
+ import { Transform, projectHierarchy, GlobalTransform, MorphWeights } from '@forgeax/engine-scene';
11
8
  import { vec3, mat4, frustum, box3 } from '@forgeax/engine-math';
9
+ import { resolveAssetHandle, walkMaterialPassesOverSharedRefs, selectMaterialPassProgram, materialParametersToParamSchema as materialParametersToParamSchema$1 } from '@forgeax/engine-assets-runtime';
12
10
  import { AssetGuid } from '@forgeax/engine-pack/guid';
13
- import { Transform, GlobalTransform, MorphWeights } from '@forgeax/engine-scene';
14
- import { standardTextureMask, DEFAULT_STANDARD_PBR_PARAM_SCHEMA, STANDARD_PIPELINE_PARAM_SCHEMA, DEFAULT_STANDARD_SURFACE_MODULE } from '@forgeax/engine-shader';
11
+ import { RhiError } from '@forgeax/engine-rhi';
15
12
  import { Skin, SkinInstancesCoexistForbiddenError, SkeletonResolveFailedError, JointCountMismatchError, JointEntityDanglingError } from '@forgeax/engine-skinning';
16
13
  import { PROCEDURAL_FLOATS_PER_VERTEX } from '@forgeax/engine-geometry';
17
14
  import { resetFontConcurrency, trackFontConcurrency, layoutGlyphText, conservativeCubeAabb, bakeGlyphMesh } from '@forgeax/engine-graphics-extras';
18
15
 
19
- // src/ssr/hiz.ts
20
- var SSR_HIZ_FORMAT = "r32float";
21
- function validExtent(extent) {
22
- return Number.isInteger(extent.width) && Number.isInteger(extent.height) && extent.width > 0 && extent.height > 0;
23
- }
24
- function requireExtent(extent) {
25
- if (!validExtent(extent)) {
26
- throw new RangeError("SSR Hi-Z extent must contain positive integer width and height");
16
+ var Atmosphere = defineComponent("Atmosphere", {
17
+ turbidity: { type: "f32", default: 2 },
18
+ rayleigh: { type: "f32", default: 1 },
19
+ mieCoefficient: { type: "f32", default: 5e-3 },
20
+ mieDirectionalG: { type: "f32", default: 0.8 },
21
+ sunAngularRadius: { type: "f32", default: 4675e-6 },
22
+ /** Independent multiplier for the analytic circumsolar lobe. */
23
+ circumsolarStrength: { type: "f32", default: 1 },
24
+ /** Relative angular width of the analytic circumsolar lobe. */
25
+ circumsolarWidth: { type: "f32", default: 1 }
26
+ });
27
+ var BarrelDistortion = defineComponent("BarrelDistortion", {
28
+ strength: { type: "f32", default: 0 },
29
+ centerX: { type: "f32", default: 0.5 },
30
+ centerY: { type: "f32", default: 0.5 }
31
+ });
32
+ var DEFAULT_BARREL_DISTORTION = Object.freeze({
33
+ strength: 0,
34
+ centerX: 0.5,
35
+ centerY: 0.5
36
+ });
37
+ function invalid(field, value, expected) {
38
+ return new BarrelDistortionInvalidParameterError({ field, value, expected });
39
+ }
40
+ function validateBarrelDistortionParameters(input) {
41
+ const value = input ?? {};
42
+ const strength = value.strength ?? DEFAULT_BARREL_DISTORTION.strength;
43
+ const centerX = value.centerX ?? DEFAULT_BARREL_DISTORTION.centerX;
44
+ const centerY = value.centerY ?? DEFAULT_BARREL_DISTORTION.centerY;
45
+ if (!Number.isFinite(strength) || strength < 0 || strength > 0.35) {
46
+ return err(invalid("strength", strength, "finite and in [0, 0.35]"));
47
+ }
48
+ if (!Number.isFinite(centerX) || centerX < 0 || centerX > 1) {
49
+ return err(invalid("centerX", centerX, "finite and in [0, 1]"));
50
+ }
51
+ if (!Number.isFinite(centerY) || centerY < 0 || centerY > 1) {
52
+ return err(invalid("centerY", centerY, "finite and in [0, 1]"));
53
+ }
54
+ return ok(Object.freeze({ strength, centerX, centerY }));
55
+ }
56
+ var CAMERA_PROJECTION_PERSPECTIVE = 0;
57
+ var CAMERA_PROJECTION_ORTHOGRAPHIC = 1;
58
+ var CAMERA_EXPOSURE_MODE_MANUAL = 0;
59
+ var CAMERA_EXPOSURE_MODE_AUTO = 1;
60
+ var CAMERA_TEMPERATURE_MIN = 1e3;
61
+ var CAMERA_TEMPERATURE_MAX = 4e4;
62
+ var CAMERA_TINT_MIN = -1;
63
+ var CAMERA_TINT_MAX = 1;
64
+ var CameraError = class extends Error {
65
+ code;
66
+ expected;
67
+ hint;
68
+ detail;
69
+ constructor(code, field, actual, expected, hint) {
70
+ super(`${code}: ${field}=${String(actual)}; expected ${expected}`);
71
+ this.name = "CameraError";
72
+ this.code = code;
73
+ this.expected = expected;
74
+ this.hint = hint;
75
+ this.detail = Object.freeze({ field, actual, expected });
76
+ }
77
+ };
78
+ function actualValue(value) {
79
+ if (typeof value === "number" || typeof value === "string") return value;
80
+ try {
81
+ return JSON.stringify(value) ?? String(value);
82
+ } catch {
83
+ return String(value);
27
84
  }
28
85
  }
29
- function nextSsrHiZMipExtent(extent) {
30
- requireExtent(extent);
31
- return {
32
- width: Math.max(1, Math.floor(extent.width / 2)),
33
- height: Math.max(1, Math.floor(extent.height / 2))
34
- };
86
+ function cameraError(code, field, actual, expected, hint) {
87
+ return new CameraError(code, field, actualValue(actual), expected, hint);
35
88
  }
36
- function buildSsrHiZPlan(extent) {
37
- requireExtent(extent);
38
- const levels = [];
39
- let current = { width: extent.width, height: extent.height };
40
- let level = 0;
41
- while (true) {
42
- levels.push({ level, ...current });
43
- if (current.width === 1 && current.height === 1) break;
44
- current = nextSsrHiZMipExtent(current);
45
- level += 1;
46
- }
47
- return Object.freeze({
48
- extent: Object.freeze({ width: extent.width, height: extent.height }),
49
- format: SSR_HIZ_FORMAT,
50
- mipLevelCount: levels.length,
51
- levels: Object.freeze(levels.map((entry) => Object.freeze(entry))),
52
- graphSize: Object.freeze({ width: extent.width, height: extent.height })
53
- });
89
+ function finitePositive(value) {
90
+ return Number.isFinite(value) && value > 0;
54
91
  }
55
-
56
- // src/ssr/resources.ts
57
- var SSR_TRACE_FORMAT = "rgba16float";
58
- var SSR_R32FLOAT_BYTES_PER_PIXEL = 4;
59
- var SSR_RGBA16FLOAT_BYTES_PER_PIXEL = 8;
60
- var SSR_TEMPORAL_PARAMS_BYTES = 32;
61
- var SSR_SPATIAL_MEMORY_BUDGET_BYTES = 45088768;
62
- function halfExtent(extent) {
63
- return {
64
- width: Math.max(1, Math.floor(extent.width / 2)),
65
- height: Math.max(1, Math.floor(extent.height / 2))
66
- };
92
+ function validateCameraExposure(exposure) {
93
+ if (typeof exposure !== "object" || exposure === null) {
94
+ throw cameraError(
95
+ "camera-exposure-invalid",
96
+ "value",
97
+ exposure,
98
+ "an object with kind manual or auto",
99
+ "choose one member of the CameraExposure union"
100
+ );
101
+ }
102
+ if (exposure.kind === "manual") {
103
+ if (!finitePositive(exposure.multiplier)) {
104
+ throw cameraError(
105
+ "camera-exposure-invalid",
106
+ "multiplier",
107
+ exposure.multiplier,
108
+ "a finite number greater than zero",
109
+ "set Camera.exposure to a positive finite manual multiplier"
110
+ );
111
+ }
112
+ return exposure;
113
+ }
114
+ if (exposure.kind === "auto") {
115
+ const [min, max] = exposure.rangeEv;
116
+ const [up, down] = exposure.rates;
117
+ if (!finitePositive(exposure.fallback) || !Number.isFinite(exposure.compensationEv) || !Number.isFinite(min) || !Number.isFinite(max) || min > max || !Number.isFinite(up) || !Number.isFinite(down) || up < 0 || down < 0) {
118
+ throw cameraError(
119
+ "camera-exposure-invalid",
120
+ "auto",
121
+ JSON.stringify(exposure),
122
+ "finite fallback/compensation, an ordered range, and non-negative rates",
123
+ "inspect fallback, compensationEv, rangeEv, and rates before rebuilding the camera"
124
+ );
125
+ }
126
+ return exposure;
127
+ }
128
+ throw cameraError(
129
+ "camera-exposure-invalid",
130
+ "kind",
131
+ exposure.kind,
132
+ "'manual' or 'auto'",
133
+ "choose one member of the CameraExposure union"
134
+ );
67
135
  }
68
- function byteCount(width, height, bytesPerPixel) {
69
- const bytes = width * height * bytesPerPixel;
70
- if (!Number.isSafeInteger(bytes)) {
71
- throw new RangeError("SSR spatial descriptor byte count exceeds safe integer range");
136
+ function validateCameraColorGrading(temperature, tint, colorLutStrength) {
137
+ if (!Number.isFinite(temperature) || temperature < CAMERA_TEMPERATURE_MIN || temperature > CAMERA_TEMPERATURE_MAX) {
138
+ throw cameraError(
139
+ "camera-color-grading-invalid",
140
+ "temperature",
141
+ temperature,
142
+ `a finite value in [${CAMERA_TEMPERATURE_MIN}, ${CAMERA_TEMPERATURE_MAX}]`,
143
+ "set Camera.temperature to a supported Kelvin value"
144
+ );
145
+ }
146
+ if (!Number.isFinite(tint) || tint < CAMERA_TINT_MIN || tint > CAMERA_TINT_MAX) {
147
+ throw cameraError(
148
+ "camera-color-grading-invalid",
149
+ "tint",
150
+ tint,
151
+ `a finite value in [${CAMERA_TINT_MIN}, ${CAMERA_TINT_MAX}]`,
152
+ "set Camera.tint to a normalized value between -1 and 1"
153
+ );
154
+ }
155
+ if (!Number.isFinite(colorLutStrength) || colorLutStrength < 0 || colorLutStrength > 1) {
156
+ throw cameraError(
157
+ "camera-color-grading-invalid",
158
+ "colorLutStrength",
159
+ colorLutStrength,
160
+ "a finite value in [0, 1]",
161
+ "set Camera.colorLutStrength to zero to disable the LUT or a blend in [0, 1]"
162
+ );
72
163
  }
73
- return bytes;
74
164
  }
75
- function estimateSsrSpatialMemory(extent, options = {}) {
76
- const half = halfExtent(extent);
77
- const plan = buildSsrHiZPlan(half);
78
- const temporal = options.temporal !== false;
79
- const hizBytes = plan.levels.reduce(
80
- (sum, level) => sum + byteCount(level.width, level.height, SSR_R32FLOAT_BYTES_PER_PIXEL),
81
- 0
165
+ function cameraProjectionFromF32(value) {
166
+ return value === CAMERA_PROJECTION_ORTHOGRAPHIC ? "orthographic" : "perspective";
167
+ }
168
+ var TONEMAP_NONE = TONEMAP_SHADER_MODE.none;
169
+ var TONEMAP_REINHARD_EXTENDED = TONEMAP_SHADER_MODE.reinhardExtended;
170
+ var TONEMAP_REINHARD = TONEMAP_SHADER_MODE.reinhard;
171
+ var TONEMAP_LINEAR = TONEMAP_SHADER_MODE.linear;
172
+ var TONEMAP_CINEON = TONEMAP_SHADER_MODE.cineon;
173
+ var TONEMAP_ACES_FILMIC = TONEMAP_SHADER_MODE.acesFilmic;
174
+ var TONEMAP_AGX = TONEMAP_SHADER_MODE.agx;
175
+ var TONEMAP_NEUTRAL = TONEMAP_SHADER_MODE.neutral;
176
+ function tonemapFromF32(value) {
177
+ switch (value) {
178
+ case TONEMAP_REINHARD_EXTENDED:
179
+ return "reinhard-extended";
180
+ case TONEMAP_REINHARD:
181
+ return "reinhard";
182
+ case TONEMAP_LINEAR:
183
+ return "linear";
184
+ case TONEMAP_CINEON:
185
+ return "cineon";
186
+ case TONEMAP_ACES_FILMIC:
187
+ return "aces-filmic";
188
+ case TONEMAP_AGX:
189
+ return "agx";
190
+ case TONEMAP_NEUTRAL:
191
+ return "neutral";
192
+ default:
193
+ return "none";
194
+ }
195
+ }
196
+ function tonemapToU32(mode) {
197
+ switch (mode) {
198
+ case "reinhard-extended":
199
+ return TONEMAP_REINHARD_EXTENDED;
200
+ case "reinhard":
201
+ return TONEMAP_REINHARD;
202
+ case "linear":
203
+ return TONEMAP_LINEAR;
204
+ case "cineon":
205
+ return TONEMAP_CINEON;
206
+ case "aces-filmic":
207
+ return TONEMAP_ACES_FILMIC;
208
+ case "agx":
209
+ return TONEMAP_AGX;
210
+ case "neutral":
211
+ return TONEMAP_NEUTRAL;
212
+ case "none":
213
+ return TONEMAP_NONE;
214
+ }
215
+ throw cameraError(
216
+ "camera-tonemap-invalid",
217
+ "tonemap",
218
+ mode,
219
+ "one of the closed Tonemap values",
220
+ "select a supported Camera tonemap mode"
82
221
  );
83
- const traceBytes = byteCount(half.width, half.height, SSR_RGBA16FLOAT_BYTES_PER_PIXEL);
84
- const hitReactivityBytes = byteCount(half.width, half.height, SSR_R32FLOAT_BYTES_PER_PIXEL);
85
- const resolvedBytes = temporal ? plan.levels.reduce(
86
- (sum, level) => sum + byteCount(level.width, level.height, SSR_RGBA16FLOAT_BYTES_PER_PIXEL),
87
- 0
88
- ) : 0;
89
- const historyBytes = temporal ? half.width * half.height * 12 * 2 : 0;
90
- const temporalParamsBytes = temporal ? SSR_TEMPORAL_PARAMS_BYTES : 0;
91
- const fallbackInputBytes = byteCount(
92
- extent.width,
93
- extent.height,
94
- SSR_RGBA16FLOAT_BYTES_PER_PIXEL
222
+ }
223
+ var ANTIALIAS_NONE = 0;
224
+ var ANTIALIAS_FXAA = 1;
225
+ var ANTIALIAS_MSAA = 2;
226
+ var ANTIALIAS_TAA = 3;
227
+ function antialiasFromF32(value) {
228
+ if (value === ANTIALIAS_NONE) return "none";
229
+ if (value === ANTIALIAS_FXAA) return "fxaa";
230
+ if (value === ANTIALIAS_MSAA) return "msaa";
231
+ if (value === ANTIALIAS_TAA) return "taa";
232
+ throw cameraError(
233
+ "camera-antialias-invalid",
234
+ "antialias",
235
+ value,
236
+ `${ANTIALIAS_NONE}, ${ANTIALIAS_FXAA}, ${ANTIALIAS_MSAA}, or ${ANTIALIAS_TAA}`,
237
+ "select a supported Camera antialias mode"
95
238
  );
96
- const ssrOwnedBytes = hizBytes + traceBytes + hitReactivityBytes + resolvedBytes + historyBytes + temporalParamsBytes + fallbackInputBytes;
97
- return Object.freeze({
98
- width: extent.width,
99
- height: extent.height,
100
- halfWidth: half.width,
101
- halfHeight: half.height,
102
- hizBytes,
103
- traceBytes,
104
- hitReactivityBytes,
105
- resolvedBytes,
106
- historyBytes,
107
- temporalParamsBytes,
108
- fallbackInputBytes,
109
- ssrOwnedBytes,
110
- budgetBytes: SSR_SPATIAL_MEMORY_BUDGET_BYTES,
111
- withinBudget: ssrOwnedBytes <= SSR_SPATIAL_MEMORY_BUDGET_BYTES
112
- });
113
239
  }
114
- function texture(graph, label, format, extent) {
115
- const created = graph.createTexture(label, {
116
- format,
117
- size: { width: extent.width, height: extent.height },
118
- domain: "linear-hdr"
119
- });
120
- if (!created.ok) return created;
121
- const view = graph.view(created.value, { label: `${label}.view`, dimension: "2d" });
122
- if (!view.ok) return view;
123
- return ok({
124
- texture: created.value,
125
- view: view.value,
126
- width: extent.width,
127
- height: extent.height,
128
- format
129
- });
240
+ var BLOOM_DISABLED = 0;
241
+ var BLOOM_ENABLED = 1;
242
+ var CAMERA_BLOOM_INTENSITY_MIN = 0;
243
+ var CAMERA_BLOOM_INTENSITY_MAX = 8;
244
+ var CAMERA_BLOOM_THRESHOLD_MIN = 0;
245
+ var CAMERA_BLOOM_THRESHOLD_MAX = 65504;
246
+ var CAMERA_BLOOM_SOFT_KNEE_MIN = 0;
247
+ var CAMERA_BLOOM_SOFT_KNEE_MAX = 1;
248
+ var CAMERA_BLOOM_SCATTER_MIN = 0;
249
+ var CAMERA_BLOOM_SCATTER_MAX = 0.95;
250
+ function bloomEnabledFromF32(value) {
251
+ if (value === BLOOM_DISABLED) return "off";
252
+ if (value === BLOOM_ENABLED) return "on";
253
+ throw cameraError(
254
+ "camera-bloom-invalid",
255
+ "bloom",
256
+ value,
257
+ `${BLOOM_DISABLED} or ${BLOOM_ENABLED}`,
258
+ "select Camera bloom off or on"
259
+ );
130
260
  }
131
- function createSsrHiZResources(graph, extent) {
132
- const plan = buildSsrHiZPlan(halfExtent(extent));
133
- const created = graph.createTexture("ssr-hiz", {
134
- format: SSR_HIZ_FORMAT,
135
- size: plan.graphSize,
136
- mipLevelCount: plan.mipLevelCount
137
- });
138
- if (!created.ok) return created;
139
- const levels = [];
140
- for (const entry of plan.levels) {
141
- const view = graph.view(created.value, {
142
- label: `ssr-hiz.mip-${entry.level}.view`,
143
- dimension: "2d",
144
- baseMipLevel: entry.level,
145
- mipLevelCount: 1
261
+ function validateBloomRange(field, value, min, max) {
262
+ if (!Number.isFinite(value) || value < min || value > max) {
263
+ throw cameraError(
264
+ "camera-bloom-invalid",
265
+ field,
266
+ value,
267
+ `a finite value in [${min}, ${max}]`,
268
+ `set Camera.${field} to a finite value in [${min}, ${max}]`
269
+ );
270
+ }
271
+ }
272
+ function validateCameraBloom(bloom, threshold, intensity, softKnee, scatter) {
273
+ const enabled = bloomEnabledFromF32(bloom);
274
+ validateBloomRange(
275
+ "bloomThreshold",
276
+ threshold,
277
+ CAMERA_BLOOM_THRESHOLD_MIN,
278
+ CAMERA_BLOOM_THRESHOLD_MAX
279
+ );
280
+ validateBloomRange(
281
+ "bloomIntensity",
282
+ intensity,
283
+ CAMERA_BLOOM_INTENSITY_MIN,
284
+ CAMERA_BLOOM_INTENSITY_MAX
285
+ );
286
+ validateBloomRange(
287
+ "bloomSoftKnee",
288
+ softKnee,
289
+ CAMERA_BLOOM_SOFT_KNEE_MIN,
290
+ CAMERA_BLOOM_SOFT_KNEE_MAX
291
+ );
292
+ validateBloomRange("bloomScatter", scatter, CAMERA_BLOOM_SCATTER_MIN, CAMERA_BLOOM_SCATTER_MAX);
293
+ return enabled;
294
+ }
295
+ var Camera = defineComponent("Camera", {
296
+ fov: { type: "f32" },
297
+ aspect: { type: "f32" },
298
+ near: { type: "f32" },
299
+ far: { type: "f32" },
300
+ projection: { type: "f32", default: 0 },
301
+ left: { type: "f32", default: -1 },
302
+ right: { type: "f32", default: 1 },
303
+ bottom: { type: "f32", default: -1 },
304
+ top: { type: "f32", default: 1 },
305
+ tonemap: { type: "f32", default: 0 },
306
+ exposure: { type: "f32", default: 1 },
307
+ exposureMode: { type: "f32", default: CAMERA_EXPOSURE_MODE_MANUAL },
308
+ compensationEv: { type: "f32", default: 0 },
309
+ rangeEv: { type: "array<f32, 2>", default: new Float32Array([-8, 8]) },
310
+ rates: { type: "array<f32, 2>", default: new Float32Array([3, 1]) },
311
+ whitePoint: { type: "f32", default: 4 },
312
+ temperature: { type: "f32", default: 6504 },
313
+ tint: { type: "f32", default: 0 },
314
+ colorLut: { type: "shared<TextureAsset>", default: 0 },
315
+ colorLutStrength: { type: "f32", default: 0 },
316
+ antialias: { type: "f32", default: 0 },
317
+ historyVersion: { type: "u32", default: 0 },
318
+ bloom: { type: "f32", default: 0 },
319
+ bloomThreshold: { type: "f32", default: 1 },
320
+ bloomIntensity: { type: "f32", default: 1 },
321
+ bloomSoftKnee: { type: "f32", default: 0.5 },
322
+ bloomScatter: { type: "f32", default: 0.7 },
323
+ // M1 target camera role: zero means this camera is eligible for display;
324
+ // non-zero shared targets are auxiliary producers whose views and receipt
325
+ // promotion remain owned by Renderer.
326
+ target: { type: "shared<RenderTarget>", simulationTransient: true },
327
+ // feat-20260709 M3 / D-3: clear-color is one inline `array<f32,4>` column.
328
+ // The earlier 4-scalar form (clearR/G/B/A) was chosen when this was believed
329
+ // to be the only SoA-safe shape; the Transform (pos/quat/scale) and light
330
+ // (direction/color) precedents disprove that -- an `array<f32,N>` IS an
331
+ // inline stride-N SoA column, read on the hot path as `col[i*N+a]` with zero
332
+ // allocation, so collapsing four scalars into one column removes three field
333
+ // names a reader must track without changing the storage layout or read
334
+ // pattern. The default `[0, 0, 0, 0]` is transparent black; explicit alpha
335
+ // stays visible through the same public array field.
336
+ clearColor: { type: "array<f32, 4>", default: new Float32Array([0, 0, 0, 0]) },
337
+ // feat-20260617-host-engine-contract-and-video-cutscene / M3 / D-4: the
338
+ // aspect-sync sidecar on the createApp(canvas) path writes
339
+ // canvas.width / canvas.height into `aspect` every frame when this flag is
340
+ // true. Reuses the existing `bool` column tier (AnimationPlayer.paused /
341
+ // AudioSource.playing precedent) -- zero ECS infrastructure change. Default
342
+ // true so demos that never touch aspect track the canvas automatically
343
+ // (charter P1 default-is-correct); set false for render-to-texture /
344
+ // split-screen cameras that drive aspect themselves. Read it via
345
+ // world.get (readRow narrows bool -> JS boolean); the query-bundle path
346
+ // returns a raw 0/1 number (the `!== 0` always-true trap).
347
+ autoAspect: { type: "bool", default: true }
348
+ });
349
+ function cameraExposureFromColumns(input) {
350
+ if (input.exposureMode === CAMERA_EXPOSURE_MODE_MANUAL) {
351
+ return validateCameraExposure({ kind: "manual", multiplier: input.exposure });
352
+ }
353
+ if (input.exposureMode === CAMERA_EXPOSURE_MODE_AUTO) {
354
+ return validateCameraExposure({
355
+ kind: "auto",
356
+ fallback: input.exposure,
357
+ compensationEv: input.compensationEv,
358
+ rangeEv: [input.rangeEv[0] ?? -8, input.rangeEv[1] ?? 8],
359
+ rates: [input.rates[0] ?? 3, input.rates[1] ?? 1]
146
360
  });
147
- if (!view.ok) return view;
148
- levels.push(view.value);
149
- }
150
- return ok({ plan, texture: created.value, levels: Object.freeze(levels) });
151
- }
152
- function createSsrSpatialResources(graph, extent) {
153
- const hiz = createSsrHiZResources(graph, extent);
154
- if (!hiz.ok) return hiz;
155
- const traceExtent = halfExtent(extent);
156
- const trace = texture(graph, "ssr-trace", SSR_TRACE_FORMAT, traceExtent);
157
- if (!trace.ok) return trace;
158
- const hitTexture = graph.createTexture("ssr-hit-reactivity", {
159
- format: "r32float",
160
- size: traceExtent
161
- });
162
- if (!hitTexture.ok) return hitTexture;
163
- const hitReactivity = graph.view(hitTexture.value, {
164
- label: "ssr-hit-reactivity.view",
165
- dimension: "2d"
166
- });
167
- if (!hitReactivity.ok) return hitReactivity;
168
- return ok({ hiz: hiz.value, trace: trace.value, hitReactivity: hitReactivity.value });
361
+ }
362
+ throw cameraError(
363
+ "camera-exposure-mode-invalid",
364
+ "exposureMode",
365
+ input.exposureMode,
366
+ `${CAMERA_EXPOSURE_MODE_MANUAL} or ${CAMERA_EXPOSURE_MODE_AUTO}`,
367
+ "set Camera.exposureMode to the manual or auto encoding"
368
+ );
169
369
  }
170
-
171
- // src/inspection-types.ts
172
- function projectLightInspection(input) {
173
- return Object.freeze({
174
- generation: input.generation,
175
- candidate: input.candidate,
176
- accepted: input.accepted,
177
- lastKnownGood: input.lastKnownGood,
178
- failure: input.failure,
179
- failureKeys: [...input.failureKeys],
180
- resourceCount: input.resourceCount,
181
- uploadBytes: input.uploadBytes
182
- });
370
+ function cameraExposureColumns(exposure) {
371
+ const checked = validateCameraExposure(exposure);
372
+ if (checked.kind === "manual") {
373
+ return {
374
+ exposureMode: CAMERA_EXPOSURE_MODE_MANUAL,
375
+ exposure: checked.multiplier,
376
+ compensationEv: 0,
377
+ rangeEv: new Float32Array([-8, 8]),
378
+ rates: new Float32Array([3, 1])
379
+ };
380
+ }
381
+ return {
382
+ exposureMode: CAMERA_EXPOSURE_MODE_AUTO,
383
+ exposure: checked.fallback,
384
+ compensationEv: checked.compensationEv,
385
+ rangeEv: new Float32Array(checked.rangeEv),
386
+ rates: new Float32Array(checked.rates)
387
+ };
183
388
  }
184
- function lightInspectionIdentity(topology, generation) {
185
- return `${topology}:generation-${generation}`;
186
- }
187
- var emptyBloomInspection = () => ({
188
- graphStatus: "empty",
189
- enabled: false,
190
- levelCount: 0,
191
- levelDimensions: [],
192
- downsamplePassCount: 0,
193
- upsamplePassCount: 0,
194
- targetCount: 0,
195
- targetBytes: 0,
196
- resourceCount: 0,
197
- passCount: 0,
198
- encodeCount: 0,
199
- bindGroupCount: 0,
200
- uploadCount: 0,
201
- residentChildBytes: 0,
202
- generation: 0,
203
- state: "off"
204
- });
205
-
206
- // src/prepare/extended-lighting/state.ts
207
- function createExtendedLightingState(generation) {
389
+ function cameraColorGradingColumns(opts) {
390
+ const exposure = opts.exposure ?? { kind: "manual", multiplier: 1 };
391
+ const temperature = opts.temperature ?? 6504;
392
+ const tint = opts.tint ?? 0;
393
+ const colorLutStrength = opts.colorLutStrength ?? 0;
394
+ validateCameraColorGrading(temperature, tint, colorLutStrength);
208
395
  return {
209
- enabled: false,
210
- generation,
211
- status: "empty",
212
- candidate: void 0,
213
- accepted: void 0,
214
- lastKnownGood: void 0,
215
- resourceCount: 0,
216
- descriptorBytes: 0,
217
- uploadCount: 0,
218
- passCount: 0,
219
- bindGroupCount: 0,
220
- failure: void 0,
221
- failureKeys: []
396
+ ...cameraExposureColumns(exposure),
397
+ temperature,
398
+ tint,
399
+ colorLut: opts.colorLut ?? 0,
400
+ colorLutStrength
222
401
  };
223
402
  }
224
- function promoteExtendedLightingCandidate(state, candidate) {
225
- if (candidate === void 0) return state;
403
+ function cameraPodFromDefaults() {
404
+ const base = {};
405
+ for (const [key, field] of Object.entries(Camera.fields)) {
406
+ if ("default" in field && field.default !== void 0) {
407
+ base[key] = field.default;
408
+ }
409
+ }
410
+ return base;
411
+ }
412
+ function perspective(opts) {
226
413
  return {
227
- ...state,
228
- enabled: true,
229
- generation: candidate.generation,
230
- status: "accepted",
231
- candidate: void 0,
232
- accepted: candidate,
233
- lastKnownGood: candidate,
234
- resourceCount: 4,
235
- descriptorBytes: candidate.descriptorBytes,
236
- uploadCount: candidate.uploadCount
414
+ // cameraPodFromDefaults() reads autoAspect's schema default (true), so an
415
+ // omitted opts.autoAspect lands the default-correct value; an explicit
416
+ // false overrides it below (D-4: factory one-step opt-out, charter P1).
417
+ ...cameraPodFromDefaults(),
418
+ fov: opts.fov,
419
+ aspect: opts.aspect,
420
+ near: opts.near ?? 0.1,
421
+ far: opts.far ?? 100,
422
+ projection: CAMERA_PROJECTION_PERSPECTIVE,
423
+ ...opts.autoAspect !== void 0 ? { autoAspect: opts.autoAspect } : {},
424
+ ...cameraColorGradingColumns(opts)
237
425
  };
238
426
  }
239
- function recordExtendedLightingFailure(state, failure2) {
240
- const key = `${failure2.detail.entity}:${failure2.detail.feature}:${failure2.detail.generation}`;
241
- if (state.failureKeys.includes(key)) return state;
427
+ function orthographic(opts) {
242
428
  return {
243
- ...state,
244
- failure: failure2,
245
- failureKeys: [...state.failureKeys, key]
429
+ ...cameraPodFromDefaults(),
430
+ fov: 0,
431
+ aspect: 1,
432
+ near: opts.near ?? 0.1,
433
+ far: opts.far ?? 100,
434
+ projection: CAMERA_PROJECTION_ORTHOGRAPHIC,
435
+ left: opts.left,
436
+ right: opts.right,
437
+ bottom: opts.bottom,
438
+ top: opts.top,
439
+ ...cameraColorGradingColumns(opts)
246
440
  };
247
441
  }
248
- function projectExtendedLightingInspection(state) {
249
- const identity = (candidate) => candidate === void 0 ? void 0 : lightInspectionIdentity(candidate.topology, candidate.generation);
250
- return projectLightInspection({
251
- generation: state.generation,
252
- candidate: identity(state.candidate),
253
- accepted: identity(state.accepted),
254
- lastKnownGood: identity(state.lastKnownGood),
255
- failure: state.failure?.code,
256
- failureKeys: state.failureKeys,
257
- resourceCount: state.resourceCount,
258
- uploadBytes: state.descriptorBytes
259
- });
442
+ var CloudQualityValue = Object.freeze({
443
+ low: 0,
444
+ medium: 1,
445
+ high: 2
446
+ });
447
+ var CloudLayer = defineComponent("CloudLayer", {
448
+ /** Stable integer seed for the periodic 3D density field. */
449
+ seed: { type: "u32", default: 1337 },
450
+ /** World-space lower edge of the layer, in metres. */
451
+ baseHeight: { type: "f32", default: 120 },
452
+ /** World-space thickness of the layer, in metres. */
453
+ thickness: { type: "f32", default: 80 },
454
+ /** Horizontal noise scale, in cycles per metre. */
455
+ scale: { type: "f32", default: 4e-3 },
456
+ /** Coverage threshold in [0, 1]. */
457
+ coverage: { type: "f32", default: 0.48 },
458
+ /** Extinction multiplier in inverse metres. */
459
+ density: { type: "f32", default: 1 },
460
+ /** World-space wind velocity in metres per second. */
461
+ wind: { type: "array<f32, 3>", default: new Float32Array([8, 0, 2]) },
462
+ /** Quality controls cache resolution and bounded ray steps. */
463
+ quality: { type: "f32", default: CloudQualityValue.medium },
464
+ /** Maximum world-space distance represented by the light-space shadow map. */
465
+ shadowRange: { type: "f32", default: 512 }
466
+ });
467
+ function cloudQualityFromF32(value) {
468
+ if (value === CloudQualityValue.low) return "low";
469
+ if (value === CloudQualityValue.medium) return "medium";
470
+ if (value === CloudQualityValue.high) return "high";
471
+ return void 0;
260
472
  }
261
- var SSR_HISTORY_FORMAT = "rgba16float";
262
- var HISTORY_USAGE = GPU_TEXTURE_USAGE_COPY_SRC | GPU_TEXTURE_USAGE_COPY_DST | GPU_TEXTURE_USAGE_TEXTURE_BINDING | GPU_TEXTURE_USAGE_STORAGE_BINDING | GPU_TEXTURE_USAGE_RENDER_ATTACHMENT;
263
- function positiveDimension(value, field) {
264
- if (!Number.isSafeInteger(value) || value <= 0) {
265
- throw new RangeError(`${field} must be a positive safe integer.`);
266
- }
267
- return value;
268
- }
269
- function createResources(device, scope, width, height) {
270
- const halfWidth = Math.max(1, Math.floor(width / 2));
271
- const halfHeight = Math.max(1, Math.floor(height / 2));
272
- const descriptor = {
273
- label: "ssr-history-rgba16float",
274
- size: { width: halfWidth, height: halfHeight, depthOrArrayLayers: 1 },
275
- format: SSR_HISTORY_FORMAT,
276
- usage: HISTORY_USAGE,
277
- mipLevelCount: 1,
278
- sampleCount: 1,
279
- dimension: "2d",
280
- viewFormats: void 0,
281
- textureBindingViewDimension: void 0
282
- };
283
- const childScope = scope.createChild(`${scope.owner}:ssr-history`);
284
- const slots = [];
285
- for (const index of [0, 1]) {
286
- const texture2 = device.createTexture({
287
- ...descriptor,
288
- label: `ssr-history-rgba16float-${index}`
289
- });
290
- if (!texture2.ok) {
291
- childScope.abandon();
292
- return texture2;
293
- }
294
- childScope._adopt("texture", texture2.value, (value) => {
295
- device.destroyTexture(value);
296
- });
297
- const view = device.createTextureView(texture2.value, {
298
- label: `ssr-history-rgba16float-${index}.view`,
299
- dimension: "2d",
300
- baseMipLevel: 0,
301
- mipLevelCount: 1,
302
- baseArrayLayer: 0,
303
- arrayLayerCount: 1
304
- });
305
- if (!view.ok) {
306
- childScope.abandon();
307
- return view;
308
- }
309
- const surfaceTexture = device.createTexture({
310
- ...descriptor,
311
- label: `ssr-history-surface-${index}`,
312
- format: "rgba8unorm"
313
- });
314
- if (!surfaceTexture.ok) {
315
- childScope.abandon();
316
- return surfaceTexture;
317
- }
318
- childScope._adopt("texture", surfaceTexture.value, (value) => {
319
- device.destroyTexture(value);
320
- });
321
- const surfaceView = device.createTextureView(surfaceTexture.value, { dimension: "2d" });
322
- if (!surfaceView.ok) {
323
- childScope.abandon();
324
- return surfaceView;
325
- }
326
- slots.push({
327
- texture: texture2.value,
328
- view: view.value,
329
- surfaceTexture: surfaceTexture.value,
330
- surfaceView: surfaceView.value
331
- });
332
- }
333
- const params = device.createBuffer({
334
- label: "ssr-temporal-params",
335
- size: 32,
336
- usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
337
- mappedAtCreation: false
338
- });
339
- if (!params.ok) {
340
- childScope.abandon();
341
- return params;
473
+ var CUBE_CAMERA_UPDATE_ONCE = 0;
474
+ var CUBE_CAMERA_UPDATE_ON_DEMAND = 1;
475
+ var CUBE_CAMERA_UPDATE_CONTINUOUS = 2;
476
+ var CUBE_CAMERA_FACE_ORDER = ["+X", "-X", "+Y", "-Y", "+Z", "-Z"];
477
+ function cubeCameraUpdateIntentFromF32(value) {
478
+ switch (value) {
479
+ case CUBE_CAMERA_UPDATE_ONCE:
480
+ return "once";
481
+ case CUBE_CAMERA_UPDATE_ON_DEMAND:
482
+ return "on-demand";
483
+ case CUBE_CAMERA_UPDATE_CONTINUOUS:
484
+ return "continuous";
485
+ default:
486
+ throw new RenderIntentInvalidError("CubeCamera", value);
487
+ }
488
+ }
489
+ function cubeCameraUpdateIntentToF32(intent) {
490
+ switch (intent) {
491
+ case "once":
492
+ return CUBE_CAMERA_UPDATE_ONCE;
493
+ case "on-demand":
494
+ return CUBE_CAMERA_UPDATE_ON_DEMAND;
495
+ case "continuous":
496
+ return CUBE_CAMERA_UPDATE_CONTINUOUS;
497
+ }
498
+ }
499
+ var CubeCamera = defineComponent("CubeCamera", {
500
+ target: { type: "shared<RenderTarget>", simulationTransient: true },
501
+ near: { type: "f32", default: 0.1 },
502
+ far: { type: "f32", default: 100 },
503
+ updateIntent: { type: "f32", default: CUBE_CAMERA_UPDATE_ONCE },
504
+ requestVersion: { type: "u32", default: 0 },
505
+ faceBudget: { type: "u32", default: 1 }
506
+ });
507
+ var DepthOfFieldQualityValue = Object.freeze({
508
+ low: 0,
509
+ medium: 1,
510
+ high: 2
511
+ });
512
+ var DepthOfFieldSideValue = Object.freeze({
513
+ both: 0,
514
+ near: 1,
515
+ far: 2
516
+ });
517
+ var DepthOfField = defineComponent("DepthOfField", {
518
+ focusDistance: { type: "f32", default: 8 },
519
+ fStop: { type: "f32", default: 2.8 },
520
+ sensorHeight: { type: "f32", default: 0.024 },
521
+ maxRadiusPixels: { type: "f32", default: 16 },
522
+ quality: { type: "f32", default: DepthOfFieldQualityValue.medium },
523
+ blurSide: { type: "f32", default: DepthOfFieldSideValue.both }
524
+ });
525
+ function depthOfFieldQualityFromF32(value) {
526
+ if (value === DepthOfFieldQualityValue.low) return "low";
527
+ if (value === DepthOfFieldQualityValue.medium) return "medium";
528
+ if (value === DepthOfFieldQualityValue.high) return "high";
529
+ return void 0;
530
+ }
531
+ function depthOfFieldSideFromF32(value) {
532
+ if (value === DepthOfFieldSideValue.both) return "both";
533
+ if (value === DepthOfFieldSideValue.near) return "near";
534
+ if (value === DepthOfFieldSideValue.far) return "far";
535
+ return void 0;
536
+ }
537
+
538
+ // src/components/directional-shadow-filter.ts
539
+ var DirectionalShadowFilterValue = Object.freeze({
540
+ pcf1: 1,
541
+ pcf3: 2,
542
+ pcf5: 3,
543
+ pcssMedium: 4,
544
+ pcssHigh: 5
545
+ });
546
+ function directionalShadowQualityFromF32(value, angularRadiusRadians, maxPenumbraTexels) {
547
+ switch (value) {
548
+ case DirectionalShadowFilterValue.pcf1:
549
+ return { kind: "pcf", kernel: 1 };
550
+ case DirectionalShadowFilterValue.pcf3:
551
+ return { kind: "pcf", kernel: 3 };
552
+ case DirectionalShadowFilterValue.pcf5:
553
+ return { kind: "pcf", kernel: 5 };
554
+ case DirectionalShadowFilterValue.pcssMedium:
555
+ return {
556
+ kind: "pcss",
557
+ preset: "medium",
558
+ angularRadiusRadians,
559
+ maxPenumbraTexels
560
+ };
561
+ case DirectionalShadowFilterValue.pcssHigh:
562
+ return {
563
+ kind: "pcss",
564
+ preset: "high",
565
+ angularRadiusRadians,
566
+ maxPenumbraTexels
567
+ };
568
+ default:
569
+ return void 0;
342
570
  }
343
- childScope._adopt("buffer", params.value, (value) => {
344
- device.destroyBuffer(value);
345
- });
346
- const cleared = device.queue.writeBuffer(params.value, 0, new Uint8Array(32));
347
- if (!cleared.ok) {
348
- childScope.abandon();
349
- return cleared;
571
+ }
572
+ var DirectionalLight = defineComponent("DirectionalLight", {
573
+ // direction is the ONLY field with no default (D-5): omitting it lands the
574
+ // array layer-3 all-zero [0,0,0], which the renderer owner rejects -- there is no
575
+ // universal default direction, so "default is illegal" forces an explicit
576
+ // non-zero value. color carries an explicit layer-2 default [1,1,1] (white);
577
+ // the array layer-3 fallback is all-zero, so the default MUST be explicit.
578
+ direction: { type: "array<f32, 3>" },
579
+ color: { type: "array<f32, 3>", default: new Float32Array([1, 1, 1]) },
580
+ intensity: { type: "f32", default: 1 },
581
+ // Shadow opt-out gate: defaults to true so zero-config spawns get shadows.
582
+ castShadow: { type: "bool", default: true },
583
+ // 9 shadow fields migrated from DirectionalLightShadow (feat-20260621 M1).
584
+ cascadeCount: { type: "f32", default: 4 },
585
+ splitLambda: { type: "f32", default: 0.75 },
586
+ cascadeBlend: { type: "f32", default: 0.2 },
587
+ mapSize: { type: "f32", default: 2048 },
588
+ depthBias: { type: "f32", default: 1e-5 },
589
+ normalBias: { type: "f32", default: 0.05 },
590
+ // Sole shadow coverage knob (feat replaces nearPlane/farPlane). The PSSM
591
+ // near end derives from the active camera near; shadowDistance is the far
592
+ // reach. Default 200 world units (matches UE-style "dynamic shadow
593
+ // distance"; the old farPlane default was 50).
594
+ shadowDistance: { type: "f32", default: 200 },
595
+ shadowFilter: {
596
+ type: "enum",
597
+ default: DirectionalShadowFilterValue.pcf3,
598
+ labels: DirectionalShadowFilterValue
599
+ },
600
+ shadowAngularRadius: { type: "f32", default: 465e-5 },
601
+ maxPenumbraTexels: { type: "f32", default: 32 }
602
+ });
603
+ var DynamicResolution = defineComponent("DynamicResolution", {
604
+ targetGpuMs: { type: "f32", default: 16.67 },
605
+ minScale: { type: "f32", default: 0.67 },
606
+ maxScale: { type: "f32", default: 1 }
607
+ });
608
+ var DEFAULT_DYNAMIC_RESOLUTION = Object.freeze({
609
+ targetGpuMs: 16.67,
610
+ minScale: 0.67,
611
+ maxScale: 1
612
+ });
613
+ function invalid2(field, value, expected) {
614
+ return new DynamicResolutionInvalidParameterError({ field, value, expected });
615
+ }
616
+ function validateDynamicResolutionParameters(input) {
617
+ const value = input ?? {};
618
+ const targetGpuMs = value.targetGpuMs ?? DEFAULT_DYNAMIC_RESOLUTION.targetGpuMs;
619
+ const minScale = value.minScale ?? DEFAULT_DYNAMIC_RESOLUTION.minScale;
620
+ const maxScale = value.maxScale ?? DEFAULT_DYNAMIC_RESOLUTION.maxScale;
621
+ if (!Number.isFinite(targetGpuMs) || targetGpuMs <= 0) {
622
+ return err(invalid2("targetGpuMs", targetGpuMs, "finite and greater than 0"));
623
+ }
624
+ if (!Number.isFinite(minScale) || minScale < 0.5 || minScale > 1) {
625
+ return err(invalid2("minScale", minScale, "finite and in [0.5, 1.0]"));
626
+ }
627
+ if (!Number.isFinite(maxScale) || maxScale < 0.5 || maxScale > 1) {
628
+ return err(invalid2("maxScale", maxScale, "finite and in [0.5, 1.0]"));
629
+ }
630
+ if (minScale > maxScale) {
631
+ return err(invalid2("minScale", minScale, "less than or equal to maxScale"));
632
+ }
633
+ return ok(Object.freeze({ targetGpuMs, minScale, maxScale }));
634
+ }
635
+ function validateDynamicResolutionCamera(input, antialias) {
636
+ if (input === void 0) return ok(void 0);
637
+ const parameters = validateDynamicResolutionParameters(input);
638
+ if (!parameters.ok) return parameters;
639
+ if (antialias !== "taa") {
640
+ return err(new DynamicResolutionRequiresTaaError({ antialias }));
641
+ }
642
+ return parameters;
643
+ }
644
+ var Fog = defineComponent("Fog", {
645
+ color: { type: "array<f32, 3>", default: new Float32Array([0.5, 0.5, 0.5]) },
646
+ density: { type: "f32", default: 0.01 },
647
+ heightFalloff: { type: "f32", default: 0 },
648
+ maxOpacity: { type: "f32", default: 1 }
649
+ });
650
+ var Instances = defineComponent("Instances", {
651
+ transforms: { type: "array<f32>" }
652
+ });
653
+ var Layer = defineComponent("Layer", {
654
+ value: { type: "i32", default: 0 }
655
+ });
656
+
657
+ // src/components/light-helpers.ts
658
+ var RANGE_ZERO_FALLBACK_INV_R2 = 1e8;
659
+ var DEG_TO_RAD = Math.PI / 180;
660
+ var PCSS_RADIUS_MIN = 1e-4;
661
+ var PCSS_RADIUS_MAX = 0.05;
662
+ var PCSS_PENUMBRA_MIN = 1;
663
+ var PCSS_PENUMBRA_MAX = 64;
664
+ function degToCos(deg) {
665
+ return Math.cos(deg * DEG_TO_RAD);
666
+ }
667
+ function computeInvRangeSquared(range) {
668
+ if (range === Number.POSITIVE_INFINITY) return 0;
669
+ if (range === 0) return RANGE_ZERO_FALLBACK_INV_R2;
670
+ return 1 / (range * range);
671
+ }
672
+ function validateDirection(componentName, direction) {
673
+ const dir = direction;
674
+ if (dir === void 0 || (dir[0] ?? 0) === 0 && (dir[1] ?? 0) === 0 && (dir[2] ?? 0) === 0) {
675
+ return new SpawnLightInvalidBoundsError(
676
+ componentName,
677
+ "direction",
678
+ dir === void 0 ? [0, 0, 0] : [dir[0] ?? 0, dir[1] ?? 0, dir[2] ?? 0]
679
+ );
350
680
  }
351
- return ok({
352
- width,
353
- height,
354
- halfWidth,
355
- halfHeight,
356
- bytes: halfWidth * halfHeight * 12 * 2,
357
- descriptor,
358
- slots,
359
- paramsBuffer: params.value,
360
- childScope
361
- });
681
+ return null;
362
682
  }
363
- function failure(code, expected, hint, detail) {
364
- return { code, expected, hint, ...{} };
365
- }
366
- var SsrHistoryOwner = class _SsrHistoryOwner {
367
- constructor(device, scope, resources) {
368
- this.device = device;
369
- this.scope = scope;
370
- this.activeResources = resources;
371
- }
372
- device;
373
- scope;
374
- activeResources;
375
- retiringResources = /* @__PURE__ */ new Set();
376
- retirementFencePending = false;
377
- activeCandidate;
378
- readIndex = 0;
379
- valid = false;
380
- state = "first-frame";
381
- resetReason = "first-enable";
382
- lastFailure;
383
- resetCount = 0;
384
- static create(input) {
385
- const width = positiveDimension(input.width, "width");
386
- const height = positiveDimension(input.height, "height");
387
- const resources = createResources(input.device, input.scope, width, height);
388
- return resources.ok ? ok(new _SsrHistoryOwner(input.device, input.scope, resources.value)) : resources;
389
- }
390
- get resources() {
391
- if (this.activeResources === void 0) throw new Error("SSR history resources are retired.");
392
- return this.activeResources;
393
- }
394
- beginFrame() {
395
- if (this.activeResources === void 0 || this.state === "retired" || this.state === "disposed") {
683
+ function validateDirectionalLightData(data) {
684
+ const directionError = validateDirection(
685
+ "DirectionalLight",
686
+ data.direction
687
+ );
688
+ if (directionError !== null) return err(directionError);
689
+ if (data.castShadow === false) return ok(void 0);
690
+ const mapSize = data.mapSize ?? 2048;
691
+ if (mapSize < 1) {
692
+ return err(new ShadowInvalidConfigError("mapSize", mapSize, 1));
693
+ }
694
+ const cascadeCount = data.cascadeCount ?? 4;
695
+ if (cascadeCount < 1 || cascadeCount > 4 || !Number.isInteger(cascadeCount)) {
696
+ return err(new ShadowInvalidConfigError("cascadeCount", cascadeCount, 1, 4));
697
+ }
698
+ const splitLambda = data.splitLambda ?? 0.75;
699
+ if (splitLambda < 0 || splitLambda > 1) {
700
+ return err(new ShadowInvalidConfigError("splitLambda", splitLambda, 0, 1));
701
+ }
702
+ const cascadeBlend = data.cascadeBlend ?? 0.2;
703
+ if (cascadeBlend < 0 || cascadeBlend > 0.5) {
704
+ return err(new ShadowInvalidConfigError("cascadeBlend", cascadeBlend, 0, 0.5));
705
+ }
706
+ const shadowDistance = data.shadowDistance ?? 200;
707
+ if (shadowDistance <= 0) {
708
+ return err(new ShadowInvalidConfigError("shadowDistance", shadowDistance, 0, ">"));
709
+ }
710
+ const shadowFilter = data.shadowFilter ?? DirectionalShadowFilterValue.pcf3;
711
+ const allowedFilters = Object.values(DirectionalShadowFilterValue);
712
+ if (!Number.isFinite(shadowFilter) || !allowedFilters.includes(shadowFilter)) {
713
+ return err(
714
+ new ShadowInvalidConfigError(
715
+ "shadowFilter",
716
+ shadowFilter,
717
+ { kind: "allowed-values", values: allowedFilters },
718
+ void 0,
719
+ "one of [pcf1, pcf3, pcf5, pcssMedium, pcssHigh]"
720
+ )
721
+ );
722
+ }
723
+ if (shadowFilter === DirectionalShadowFilterValue.pcssMedium || shadowFilter === DirectionalShadowFilterValue.pcssHigh) {
724
+ const shadowAngularRadius = data.shadowAngularRadius ?? 465e-5;
725
+ if (!Number.isFinite(shadowAngularRadius)) {
396
726
  return err(
397
- failure(
398
- "ssr-history-retired",
399
- "SSR history resources are active",
400
- "recreate the renderer-owned SSR history after device recovery"
401
- )
727
+ new ShadowInvalidConfigError("shadowAngularRadius", shadowAngularRadius, {
728
+ kind: "range",
729
+ min: PCSS_RADIUS_MIN,
730
+ max: PCSS_RADIUS_MAX
731
+ })
402
732
  );
403
733
  }
404
- if (this.activeCandidate !== void 0) {
734
+ if (shadowAngularRadius < Math.fround(PCSS_RADIUS_MIN) || shadowAngularRadius > Math.fround(PCSS_RADIUS_MAX)) {
405
735
  return err(
406
- failure(
407
- "ssr-history-active",
408
- "one SSR history candidate is active",
409
- "commit or abort the active SSR history candidate before beginning another"
410
- )
736
+ new ShadowInvalidConfigError("shadowAngularRadius", shadowAngularRadius, {
737
+ kind: "range",
738
+ min: PCSS_RADIUS_MIN,
739
+ max: PCSS_RADIUS_MAX
740
+ })
411
741
  );
412
742
  }
413
- const writeSlot = this.valid ? this.readIndex === 0 ? 1 : 0 : 0;
414
- const candidate = Object.freeze({
415
- readSlot: this.valid ? this.readIndex : null,
416
- writeSlot,
417
- historyValid: this.valid && this.resetReason === void 0
418
- });
419
- this.activeCandidate = candidate;
420
- this.state = this.valid ? "stable" : this.state === "aborted" ? "aborted" : "first-frame";
421
- this.lastFailure = void 0;
422
- return ok(candidate);
423
- }
424
- commitFrame(candidate) {
425
- if (this.activeCandidate !== candidate) {
743
+ const maxPenumbraTexels = data.maxPenumbraTexels ?? 32;
744
+ if (!Number.isFinite(maxPenumbraTexels) || !Number.isInteger(maxPenumbraTexels) || maxPenumbraTexels < PCSS_PENUMBRA_MIN || maxPenumbraTexels > PCSS_PENUMBRA_MAX) {
426
745
  return err(
427
- failure(
428
- "ssr-history-not-active",
429
- "commit the candidate returned by beginFrame",
430
- "keep one candidate from beginFrame until the shared queue submit succeeds"
746
+ new ShadowInvalidConfigError(
747
+ "maxPenumbraTexels",
748
+ maxPenumbraTexels,
749
+ { kind: "range", min: PCSS_PENUMBRA_MIN, max: PCSS_PENUMBRA_MAX },
750
+ void 0,
751
+ "a finite integer in [1, 64]"
431
752
  )
432
753
  );
433
754
  }
434
- this.readIndex = candidate.writeSlot;
435
- this.valid = true;
436
- this.activeCandidate = void 0;
437
- this.state = "stable";
438
- this.resetReason = void 0;
439
- this.lastFailure = void 0;
440
- return ok(void 0);
441
755
  }
442
- abortFrame(candidate, stage) {
443
- if (this.activeCandidate !== candidate) return;
444
- this.activeCandidate = void 0;
445
- this.state = "aborted";
446
- this.lastFailure = stage;
447
- }
448
- reset(reason) {
449
- this.activeCandidate = void 0;
450
- this.valid = false;
451
- this.readIndex = 0;
452
- this.resetReason = reason;
453
- this.lastFailure = void 0;
454
- this.resetCount += 1;
455
- if (this.activeResources !== void 0) this.state = "reset";
456
- }
457
- resize(width, height) {
458
- const next = createResources(
459
- this.device,
460
- this.scope,
461
- positiveDimension(width, "width"),
462
- positiveDimension(height, "height")
756
+ return ok(void 0);
757
+ }
758
+ function validateSpotLightData(data) {
759
+ const directionError = validateDirection(
760
+ "SpotLight",
761
+ data.direction
762
+ );
763
+ if (directionError !== null) return err(directionError);
764
+ if (data.castShadow === false) return ok(void 0);
765
+ const range = data.range ?? 10;
766
+ if (typeof range !== "number" || Number.isNaN(range) || range < 0) {
767
+ return err(new SpawnLightInvalidBoundsError("SpotLight", "range", range));
768
+ }
769
+ const innerConeDeg = data.innerConeDeg ?? 0;
770
+ const outerConeDeg = data.outerConeDeg ?? 45;
771
+ if (outerConeDeg > 90) {
772
+ return err(new SpawnLightInvalidBoundsError("SpotLight", "outerNinety", outerConeDeg));
773
+ }
774
+ if (outerConeDeg <= innerConeDeg) {
775
+ return err(new SpawnLightInvalidBoundsError("SpotLight", "innerOuter", outerConeDeg));
776
+ }
777
+ const mapSize = data.mapSize ?? 2048;
778
+ if (mapSize < 1) return err(new ShadowInvalidConfigError("mapSize", mapSize, 1));
779
+ const nearPlane = data.nearPlane;
780
+ const farPlane = data.farPlane;
781
+ if (nearPlane !== void 0 && farPlane !== void 0 && farPlane <= nearPlane) {
782
+ return err(new ShadowInvalidConfigError("farPlane", farPlane, nearPlane));
783
+ }
784
+ const pcfKernelSize = data.pcfKernelSize ?? 3;
785
+ if (pcfKernelSize < 1 || pcfKernelSize % 2 === 0) {
786
+ return err(
787
+ new ShadowInvalidConfigError(
788
+ "pcfKernelSize",
789
+ pcfKernelSize,
790
+ { kind: "lower-bound", operator: ">=", value: 1 },
791
+ void 0,
792
+ "an odd integer >= 1"
793
+ )
463
794
  );
464
- if (!next.ok) return next;
465
- if (this.activeResources !== void 0) this.retiringResources.add(this.activeResources);
466
- this.activeResources = next.value;
467
- this.activeCandidate = void 0;
468
- this.valid = false;
469
- this.readIndex = 0;
470
- this.resetReason = "resize";
471
- this.lastFailure = void 0;
472
- this.resetCount += 1;
473
- this.state = "reset";
474
- return ok(void 0);
475
795
  }
476
- /** Retire pending allocations after the queue's already-submitted work. */
477
- retireAfterFence(queue, onFailure) {
478
- if (this.retirementFencePending) return;
479
- if (this.retiringResources.size === 0 && this.activeResources !== void 0) {
480
- this.retiringResources.add(this.activeResources);
481
- this.activeCandidate = void 0;
482
- this.valid = false;
483
- this.state = "retiring";
484
- }
485
- const pending = [...this.retiringResources];
486
- for (const resources of pending) resources.childScope.beginRetire();
487
- if (pending.length === 0) return;
488
- this.retirementFencePending = true;
489
- const finish = () => {
490
- this.retirementFencePending = false;
491
- for (const resources of pending) {
492
- this.retiringResources.delete(resources);
493
- resources.childScope.retire();
494
- if (this.activeResources === resources) this.activeResources = void 0;
495
- }
496
- if (this.activeResources === void 0 && this.retiringResources.size === 0) {
497
- this.state = "retired";
498
- }
499
- if (this.retiringResources.size > 0) this.retireAfterFence(queue, onFailure);
500
- };
501
- void queue.onSubmittedWorkDone().then(finish, (cause) => {
502
- onFailure(cause);
503
- finish();
504
- });
796
+ const shadowIntensity = data.shadowIntensity ?? 1;
797
+ if (!Number.isFinite(shadowIntensity) || shadowIntensity < 0 || shadowIntensity > 1) {
798
+ return err(new ShadowInvalidConfigError("shadowIntensity", shadowIntensity, 0, 1));
505
799
  }
506
- retire() {
507
- this.retirementFencePending = false;
508
- this.activeCandidate = void 0;
509
- if (this.activeResources !== void 0) {
510
- this.activeResources.childScope.retire();
511
- this.activeResources = void 0;
512
- }
513
- for (const resources of this.retiringResources) resources.childScope.retire();
514
- this.retiringResources.clear();
515
- this.valid = false;
516
- this.state = "retired";
800
+ return ok(void 0);
801
+ }
802
+ function validatePointLightData(data) {
803
+ const range = data.range ?? 10;
804
+ if (typeof range !== "number" || Number.isNaN(range) || range < 0) {
805
+ return err(new SpawnLightInvalidBoundsError("PointLight", "range", range));
517
806
  }
518
- dispose() {
519
- if (this.state === "disposed") return;
520
- this.retire();
521
- this.state = "disposed";
807
+ return ok(void 0);
808
+ }
809
+ function validatePointLightShadowData(data) {
810
+ const mapSize = data.mapSize ?? 512;
811
+ if (mapSize < 1) return err(new ShadowInvalidConfigError("mapSize", mapSize, 1));
812
+ const nearPlane = data.nearPlane ?? 0.1;
813
+ const farPlane = data.farPlane ?? 25;
814
+ if (farPlane <= nearPlane) {
815
+ return err(new ShadowInvalidConfigError("farPlane", farPlane, nearPlane));
816
+ }
817
+ const pcfKernelSize = data.pcfKernelSize ?? 3;
818
+ if (pcfKernelSize < 1 || pcfKernelSize % 2 === 0) {
819
+ return err(
820
+ new ShadowInvalidConfigError(
821
+ "pcfKernelSize",
822
+ pcfKernelSize,
823
+ { kind: "lower-bound", operator: ">=", value: 1 },
824
+ void 0,
825
+ "an odd integer >= 1"
826
+ )
827
+ );
522
828
  }
523
- inspect() {
524
- const active = this.activeResources;
525
- return Object.freeze({
526
- state: this.state,
527
- width: active?.width ?? 0,
528
- height: active?.height ?? 0,
529
- halfWidth: active?.halfWidth ?? 0,
530
- halfHeight: active?.halfHeight ?? 0,
531
- format: SSR_HISTORY_FORMAT,
532
- historyCount: active === void 0 ? 0 : 2,
533
- historyValid: this.valid,
534
- readSlot: active === void 0 ? null : this.valid ? this.readIndex : null,
535
- writeSlot: active === void 0 ? 0 : this.valid ? this.readIndex === 0 ? 1 : 0 : 0,
536
- activeBytes: active?.bytes ?? 0,
537
- candidateBytes: 0,
538
- retiringBytes: [...this.retiringResources].filter((entry) => entry !== active).reduce((sum, entry) => sum + entry.bytes, 0),
539
- resetCount: this.resetCount,
540
- resetReason: this.resetReason,
541
- lastFailure: this.lastFailure
542
- });
829
+ return ok(void 0);
830
+ }
831
+ function validateRectAreaLightData(data) {
832
+ const intensity = data.intensity ?? 1;
833
+ if (typeof intensity !== "number" || !Number.isFinite(intensity) || intensity < 0) {
834
+ return err(new SpawnLightInvalidBoundsError("RectAreaLight", "intensity", intensity));
835
+ }
836
+ const color = data.color ?? [1, 1, 1];
837
+ if (color.length !== 3 || Array.from(color).some(
838
+ (value) => typeof value !== "number" || !Number.isFinite(value) || value < 0
839
+ )) {
840
+ return err(new SpawnLightInvalidBoundsError("RectAreaLight", "color", Array.from(color)));
841
+ }
842
+ for (const field of ["width", "height", "range"]) {
843
+ const value = data[field] ?? (field === "range" ? 10 : 1);
844
+ if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
845
+ return err(new SpawnLightInvalidBoundsError("RectAreaLight", field, value));
846
+ }
543
847
  }
544
- };
545
- function createSsrHistoryOwner(input) {
546
- return SsrHistoryOwner.create(input);
848
+ return ok(void 0);
547
849
  }
548
-
549
- // src/scene/visibility/occlusion-query-pool.ts
550
- var OCCLUSION_QUERY_PAGE_COUNT = 3;
551
- var OCCLUSION_QUERY_PAGE_INDEX_LIMIT = 4096;
552
- var OCCLUSION_QUERY_RESULT_BYTES = 8;
553
- var OCCLUSION_QUERY_PAGE_BYTES = OCCLUSION_QUERY_PAGE_INDEX_LIMIT * OCCLUSION_QUERY_RESULT_BYTES;
554
- function identityKey(identity) {
555
- return [
556
- identity.viewKey,
557
- identity.attachmentId,
558
- identity.deviceGeneration,
559
- identity.worldGeneration,
560
- identity.primitiveSlot,
561
- identity.slotGeneration
562
- ].join("|");
563
- }
564
- var OcclusionQueryPool = class {
565
- pages = Array.from(
566
- { length: OCCLUSION_QUERY_PAGE_COUNT },
567
- (_, index) => ({ index, nextQueryIndex: 0, pendingCount: 0, inFlightCount: 0 })
568
- );
569
- pending = /* @__PURE__ */ new Map();
570
- tickets = /* @__PURE__ */ new Map();
571
- nextReservationId = 1;
572
- disposed = false;
573
- reserve(identity) {
574
- if (this.disposed) return void 0;
575
- const page = this.pages.find(
576
- (candidate) => candidate.inFlightCount === 0 && candidate.nextQueryIndex < OCCLUSION_QUERY_PAGE_INDEX_LIMIT
850
+ function validateLightProbeData(data) {
851
+ const irradiance = data.irradiance;
852
+ if (irradiance === void 0 || irradiance.length !== 27 || Array.from(irradiance).some((value) => typeof value !== "number" || !Number.isFinite(value))) {
853
+ return err(
854
+ new SpawnLightInvalidBoundsError(
855
+ "LightProbe",
856
+ "irradiance",
857
+ irradiance === void 0 ? [] : Array.from(irradiance)
858
+ )
577
859
  );
578
- if (page === void 0 || page.nextQueryIndex >= OCCLUSION_QUERY_PAGE_INDEX_LIMIT) {
579
- return void 0;
860
+ }
861
+ const radius = data.radius ?? R_MIN;
862
+ if (typeof radius !== "number" || !Number.isFinite(radius) || radius < R_MIN) {
863
+ return err(new SpawnLightInvalidBoundsError("LightProbe", "radius", radius));
864
+ }
865
+ return ok(void 0);
866
+ }
867
+ var LightProbe = defineComponent("LightProbe", {
868
+ irradiance: { type: "array<f32, 27>" },
869
+ radius: { type: "f32", default: R_MIN }
870
+ });
871
+ var Lines = defineComponent(
872
+ "Lines",
873
+ {
874
+ widthPx: { type: "f32", default: 1 }
875
+ },
876
+ {
877
+ meta: {
878
+ quickStart: "Attach Lines to render line-list MeshAsset pairs in screen pixels.",
879
+ diagnostics: "Inspect widthPx at the points-lines admission boundary.",
880
+ recovery: "Use points-lines-invalid-style for non-finite or non-positive widthPx values.",
881
+ boundaries: "Lines owns raster width only; MeshFilter and MeshRenderer own geometry and material."
580
882
  }
581
- const reservation = Object.freeze({
582
- ...identity,
583
- pageIndex: page.index,
584
- queryIndex: page.nextQueryIndex,
585
- reservationId: this.nextReservationId++
586
- });
587
- page.nextQueryIndex += 1;
588
- page.pendingCount += 1;
589
- this.pending.set(reservation.reservationId, reservation);
590
- return reservation;
591
- }
592
- publish(reservation, result) {
593
- const pending = this.pending.get(reservation.reservationId);
594
- if (pending === void 0 || this.disposed) {
883
+ }
884
+ );
885
+ var MeshFilter = defineComponent("MeshFilter", {
886
+ assetHandle: { type: "shared<MeshAsset>" }
887
+ });
888
+ var MeshRenderer = defineComponent("MeshRenderer", {
889
+ materials: { type: "array<shared<MaterialAsset>>", default: [] }
890
+ });
891
+ var MotionBlur = defineComponent("MotionBlur", {
892
+ shutterAngle: { type: "f32", default: 180 },
893
+ maxRadiusPixels: { type: "f32", default: 32 },
894
+ sampleCount: { type: "f32", default: 8 },
895
+ targetFps: { type: "f32", default: 60 }
896
+ });
897
+ var PointLight = defineComponent("PointLight", {
898
+ // color carries an explicit layer-2 default [1,1,1] (white); the array
899
+ // layer-3 fallback is all-zero, so the default MUST be explicit (D-5).
900
+ color: { type: "array<f32, 3>", default: new Float32Array([1, 1, 1]) },
901
+ intensity: { type: "f32", default: 1 },
902
+ range: { type: "f32", default: 10 }
903
+ });
904
+ var PointLightShadow = defineComponent("PointLightShadow", {
905
+ mapSize: { type: "f32", default: 512 },
906
+ depthBias: { type: "f32", default: 5e-3 },
907
+ normalBias: { type: "f32", default: 0.05 },
908
+ nearPlane: { type: "f32", default: 0.1 },
909
+ farPlane: { type: "f32", default: 25 },
910
+ pcfKernelSize: { type: "f32", default: 3 }
911
+ });
912
+ var PointShapeValue = Object.freeze({
913
+ square: 0,
914
+ circle: 1
915
+ });
916
+ function pointShapeFromU32(value) {
917
+ switch (value) {
918
+ case PointShapeValue.square:
919
+ return "square";
920
+ case PointShapeValue.circle:
921
+ return "circle";
922
+ default:
595
923
  return void 0;
924
+ }
925
+ }
926
+ var Points = defineComponent(
927
+ "Points",
928
+ {
929
+ sizePx: { type: "f32", default: 4 },
930
+ shape: { type: "enum", default: PointShapeValue.square, labels: PointShapeValue }
931
+ },
932
+ {
933
+ meta: {
934
+ quickStart: "Attach Points to render point-list MeshAsset vertices in screen pixels.",
935
+ diagnostics: "Inspect sizePx and decode shape with pointShapeFromU32.",
936
+ recovery: "Use points-lines-invalid-style for non-finite or non-positive sizePx values.",
937
+ boundaries: "Points owns raster size and shape only; MeshFilter and MeshRenderer own geometry and material."
596
938
  }
597
- if (!result.submitted) return void 0;
598
- this.pending.delete(reservation.reservationId);
599
- const page = this.pages[pending.pageIndex];
600
- if (page !== void 0) {
601
- page.pendingCount = Math.max(0, page.pendingCount - 1);
602
- page.inFlightCount += 1;
939
+ }
940
+ );
941
+ var schema = {
942
+ shader: "string",
943
+ data: "buffer"
944
+ };
945
+ var PostProcessParams = defineComponent("PostProcessParams", schema);
946
+ var RectAreaLight = defineComponent("RectAreaLight", {
947
+ color: { type: "array<f32, 3>", default: new Float32Array([1, 1, 1]) },
948
+ intensity: { type: "f32", default: 1 },
949
+ width: { type: "f32", default: 1 },
950
+ height: { type: "f32", default: 1 },
951
+ range: { type: "f32", default: 10 }
952
+ });
953
+ var REFLECTION_PROBE_UPDATE_ONCE = 0;
954
+ var REFLECTION_PROBE_UPDATE_ON_CHANGE = 1;
955
+ var REFLECTION_PROBE_UPDATE_CONTINUOUS = 2;
956
+ function reflectionProbeUpdateIntentFromF32(value) {
957
+ switch (value) {
958
+ case REFLECTION_PROBE_UPDATE_ONCE:
959
+ return "once";
960
+ case REFLECTION_PROBE_UPDATE_ON_CHANGE:
961
+ return "on-change";
962
+ case REFLECTION_PROBE_UPDATE_CONTINUOUS:
963
+ return "continuous";
964
+ default:
965
+ throw new RenderIntentInvalidError("ReflectionProbe", value);
966
+ }
967
+ }
968
+ function reflectionProbeUpdateIntentToF32(intent) {
969
+ switch (intent) {
970
+ case "once":
971
+ return REFLECTION_PROBE_UPDATE_ONCE;
972
+ case "on-change":
973
+ return REFLECTION_PROBE_UPDATE_ON_CHANGE;
974
+ case "continuous":
975
+ return REFLECTION_PROBE_UPDATE_CONTINUOUS;
976
+ }
977
+ }
978
+ var ReflectionProbe = defineComponent("ReflectionProbe", {
979
+ halfExtents: { type: "array<f32, 3>", default: new Float32Array([1, 1, 1]) },
980
+ priority: { type: "f32", default: 0 },
981
+ boxProjection: { type: "bool", default: false },
982
+ intensity: { type: "f32", default: 1 },
983
+ resolution: { type: "u32", default: 256 },
984
+ updateIntent: { type: "f32", default: REFLECTION_PROBE_UPDATE_ONCE },
985
+ invalidationVersion: { type: "u32", default: 0 }
986
+ });
987
+ var SceneInstance = defineComponent(
988
+ "SceneInstance",
989
+ {
990
+ source: { type: "shared<SceneAsset>" },
991
+ mapping: { type: "array<entity>" },
992
+ // The unique slot is the ECS storage seam for the instance's structured
993
+ // runtime payload; keep the nested semantic visible to schema consumers.
994
+ state: { type: "unique<SceneInstanceState>", shape: "nested" }
995
+ },
996
+ { transient: true }
997
+ );
998
+ var ScreenSpaceReflection = defineComponent("ScreenSpaceReflection", {
999
+ maxDistance: { type: "f32", default: 40 },
1000
+ thickness: { type: "f32", default: 0.2 },
1001
+ maxRoughness: { type: "f32", default: 0.6 }
1002
+ });
1003
+ var SKYBOX_MODE_CUBEMAP = 0;
1004
+ var SkyboxBackground = defineComponent("SkyboxBackground", {
1005
+ // The GPU skybox projection is render-owned presentation state; restore
1006
+ // keeps the portable mode/rotation controls and lets the owner re-resolve
1007
+ // the environment asset on the target world.
1008
+ equirect: { type: "shared<EquirectAsset>", simulationTransient: true },
1009
+ mode: { type: "f32", default: SKYBOX_MODE_CUBEMAP },
1010
+ rotation: { type: "array<f32, 4>", default: new Float32Array([0, 0, 0, 1]) }
1011
+ });
1012
+ var Skylight = defineComponent("Skylight", {
1013
+ // The GPU equirect-to-cubemap projection is render-owned presentation
1014
+ // state. Record/restore preserves the portable lighting controls while the
1015
+ // render owner resolves this asset again on the target world.
1016
+ equirect: { type: "shared<EquirectAsset>", simulationTransient: true },
1017
+ // color carries an explicit layer-2 default [1,1,1] (white); the array
1018
+ // layer-3 fallback is all-zero, so the default MUST be explicit (D-5).
1019
+ color: { type: "array<f32, 3>", default: new Float32Array([1, 1, 1]) },
1020
+ intensity: { type: "f32", default: 1 },
1021
+ rotation: { type: "array<f32, 4>", default: new Float32Array([0, 0, 0, 1]) }
1022
+ });
1023
+ var SortKey = defineComponent("SortKey", {
1024
+ value: { type: "f32", default: 0 }
1025
+ });
1026
+ var SpotLight = defineComponent("SpotLight", {
1027
+ // direction has no default (D-5): omitting it lands the array layer-3
1028
+ // all-zero, which validate() rejects. color carries an explicit layer-2
1029
+ // default [1,1,1] (white); the array layer-3 fallback is all-zero.
1030
+ direction: { type: "array<f32, 3>" },
1031
+ color: { type: "array<f32, 3>", default: new Float32Array([1, 1, 1]) },
1032
+ intensity: { type: "f32", default: 1 },
1033
+ range: { type: "f32", default: 10 },
1034
+ innerConeDeg: { type: "f32", default: 0 },
1035
+ outerConeDeg: { type: "f32", default: 45 },
1036
+ iesProfile: { type: "shared<IesProfileAsset>" },
1037
+ cookie: { type: "shared<TextureAsset>" },
1038
+ rollDeg: { type: "f32", default: 0 },
1039
+ // Shadow opt-out gate: defaults to true so zero-config spawns cast shadows.
1040
+ castShadow: { type: "bool", default: true },
1041
+ // 7 shadow fields aligned with the shared light shadow vocabulary.
1042
+ mapSize: { type: "f32", default: 2048 },
1043
+ depthBias: { type: "f32", default: 5e-3 },
1044
+ normalBias: { type: "f32", default: 0.05 },
1045
+ nearPlane: { type: "f32", default: 0.1 },
1046
+ farPlane: { type: "f32", default: 50 },
1047
+ pcfKernelSize: { type: "f32", default: 3 },
1048
+ // Three's SpotLight.shadow.intensity is the fraction of visibility applied
1049
+ // to a shadowed contribution. Keep it in the author-facing [0,1] domain;
1050
+ // extract/record transport this one fact to surface and volume consumers.
1051
+ shadowIntensity: { type: "f32", default: 1 },
1052
+ // Shared TextureAsset identity; publication and residency remain owned by
1053
+ // the existing asset and GPU resource owners.
1054
+ projector: { type: "shared<TextureAsset>", simulationTransient: true }
1055
+ });
1056
+ var VisibilityStateValue = Object.freeze({
1057
+ inherited: 0,
1058
+ hidden: 1,
1059
+ visible: 2
1060
+ });
1061
+ function visibilityStateFromU32(value) {
1062
+ switch (value) {
1063
+ case VisibilityStateValue.inherited:
1064
+ return "inherited";
1065
+ case VisibilityStateValue.hidden:
1066
+ return "hidden";
1067
+ case VisibilityStateValue.visible:
1068
+ return "visible";
1069
+ default:
1070
+ return void 0;
1071
+ }
1072
+ }
1073
+ var Visibility = defineComponent(
1074
+ "Visibility",
1075
+ {
1076
+ state: {
1077
+ type: "enum",
1078
+ default: VisibilityStateValue.inherited,
1079
+ labels: VisibilityStateValue
603
1080
  }
604
- const ticket = Object.freeze({ ...pending, submissionGeneration: result.submissionGeneration });
605
- this.tickets.set(ticket.reservationId, ticket);
606
- return ticket;
607
- }
608
- /** Return an unsubmitted reservation to its bounded page. */
609
- cancel(reservation) {
610
- const pending = this.pending.get(reservation.reservationId);
611
- if (pending === void 0) return false;
612
- this.pending.delete(reservation.reservationId);
613
- const page = this.pages[pending.pageIndex];
614
- if (page !== void 0) {
615
- page.pendingCount = Math.max(0, page.pendingCount - 1);
616
- this.releasePage(pending.pageIndex);
1081
+ },
1082
+ {
1083
+ meta: {
1084
+ quickStart: "Attach Visibility to author render participation intent.",
1085
+ diagnostics: "Compare the ECS state with resolveVisibility and renderer.visibilityStats.",
1086
+ recovery: "Use structured ECS write errors and repair the owning scene relation.",
1087
+ boundaries: "Visibility does not own camera, picking, lifecycle, assets, or VFX shadow policy."
617
1088
  }
618
- return true;
619
1089
  }
620
- complete(ticket, _samples) {
621
- const current = this.tickets.get(ticket.reservationId);
622
- if (current !== void 0) {
623
- this.tickets.delete(ticket.reservationId);
624
- const page = this.pages[current.pageIndex];
625
- if (page !== void 0) page.inFlightCount = Math.max(0, page.inFlightCount - 1);
626
- this.releasePage(current.pageIndex);
1090
+ );
1091
+ var COMPUTED_UV_BOUNDARY_EPSILON = Number.EPSILON * 16;
1092
+ function freezeBarrelDistortionMapping(mapping) {
1093
+ const camera = mapping.camera;
1094
+ return Object.freeze({
1095
+ ...mapping,
1096
+ ...camera === void 0 ? {} : {
1097
+ camera: Object.freeze({
1098
+ projection: camera.projection,
1099
+ far: camera.far,
1100
+ viewMatrix: Object.freeze(Array.from(camera.viewMatrix)),
1101
+ projectionMatrix: Object.freeze(Array.from(camera.projectionMatrix))
1102
+ })
627
1103
  }
628
- if (this.disposed || current === void 0 || identityKey(current) !== identityKey(ticket)) {
629
- return { status: "stale", visible: true };
1104
+ });
1105
+ }
1106
+ function attachBarrelDistortionCameraFrame(mapping, camera) {
1107
+ return freezeBarrelDistortionMapping({
1108
+ ...mapping,
1109
+ camera: {
1110
+ projection: camera.projection,
1111
+ far: camera.far,
1112
+ viewMatrix: camera.viewMatrix,
1113
+ projectionMatrix: camera.projectionMatrix
630
1114
  }
631
- return { status: "accepted", visible: true };
1115
+ });
1116
+ }
1117
+ function invalidExtent(field, value) {
1118
+ return new BarrelDistortionInvalidParameterError({
1119
+ field,
1120
+ value,
1121
+ expected: "finite and greater than 0"
1122
+ });
1123
+ }
1124
+ function createBarrelDistortionMapping(width, height, input) {
1125
+ if (!Number.isFinite(width) || width <= 0) return err(invalidExtent("width", width));
1126
+ if (!Number.isFinite(height) || height <= 0) return err(invalidExtent("height", height));
1127
+ const parameters = validateBarrelDistortionParameters(input);
1128
+ if (!parameters.ok) return parameters;
1129
+ const { strength, centerX, centerY } = parameters.value;
1130
+ const aspect = width / height;
1131
+ const radiusSquared = 4 * (aspect * aspect * Math.max(centerX, 1 - centerX) ** 2 + Math.max(centerY, 1 - centerY) ** 2);
1132
+ return ok(
1133
+ Object.freeze({
1134
+ width,
1135
+ height,
1136
+ aspect,
1137
+ strength,
1138
+ centerX,
1139
+ centerY,
1140
+ radiusSquared
1141
+ })
1142
+ );
1143
+ }
1144
+ function displayToSceneScale(mapping, ux, uy) {
1145
+ const px = 2 * mapping.aspect * (ux - mapping.centerX);
1146
+ const py = 2 * (uy - mapping.centerY);
1147
+ const t = (px * px + py * py) / mapping.radiusSquared;
1148
+ return (1 - mapping.strength) / (1 - mapping.strength * t);
1149
+ }
1150
+ function normalizeComputedUv(out, x, y) {
1151
+ if (!Number.isFinite(x) || !Number.isFinite(y)) return false;
1152
+ if (x < -COMPUTED_UV_BOUNDARY_EPSILON || x > 1 + COMPUTED_UV_BOUNDARY_EPSILON || y < -COMPUTED_UV_BOUNDARY_EPSILON || y > 1 + COMPUTED_UV_BOUNDARY_EPSILON) {
1153
+ return false;
632
1154
  }
633
- inspect() {
634
- return {
635
- pageCount: OCCLUSION_QUERY_PAGE_COUNT,
636
- pageIndexLimit: OCCLUSION_QUERY_PAGE_INDEX_LIMIT,
637
- resolveBytes: OCCLUSION_QUERY_PAGE_BYTES,
638
- stagingBytes: OCCLUSION_QUERY_PAGE_BYTES,
639
- availablePages: this.disposed ? 0 : this.pages.filter(
640
- (page) => page.inFlightCount === 0 && page.nextQueryIndex < OCCLUSION_QUERY_PAGE_INDEX_LIMIT
641
- ).length,
642
- inFlight: this.tickets.size,
643
- disposed: this.disposed
644
- };
1155
+ out.x = Math.min(1, Math.max(0, x));
1156
+ out.y = Math.min(1, Math.max(0, y));
1157
+ return true;
1158
+ }
1159
+ function mapDisplayUvToSceneUv(out, mapping, ux, uy) {
1160
+ if (!Number.isFinite(ux) || !Number.isFinite(uy) || ux < 0 || ux > 1 || uy < 0 || uy > 1)
1161
+ return false;
1162
+ if (mapping.strength === 0) {
1163
+ out.x = ux;
1164
+ out.y = uy;
1165
+ return ux >= 0 && ux <= 1 && uy >= 0 && uy <= 1;
1166
+ }
1167
+ const scale = displayToSceneScale(mapping, ux, uy);
1168
+ return normalizeComputedUv(
1169
+ out,
1170
+ mapping.centerX + (ux - mapping.centerX) * scale,
1171
+ mapping.centerY + (uy - mapping.centerY) * scale
1172
+ );
1173
+ }
1174
+ function mapSceneUvToDisplayUv(out, mapping, ux, uy) {
1175
+ if (!Number.isFinite(ux) || !Number.isFinite(uy) || ux < 0 || ux > 1 || uy < 0 || uy > 1)
1176
+ return false;
1177
+ if (mapping.strength === 0) {
1178
+ out.x = ux;
1179
+ out.y = uy;
1180
+ return ux >= 0 && ux <= 1 && uy >= 0 && uy <= 1;
1181
+ }
1182
+ const px = 2 * mapping.aspect * (ux - mapping.centerX);
1183
+ const py = 2 * (uy - mapping.centerY);
1184
+ const sourceRadius = Math.hypot(px, py);
1185
+ if (sourceRadius === 0) {
1186
+ out.x = mapping.centerX;
1187
+ out.y = mapping.centerY;
1188
+ return true;
645
1189
  }
646
- dispose() {
647
- this.disposed = true;
648
- this.pending.clear();
649
- this.tickets.clear();
650
- for (const page of this.pages) {
651
- page.pendingCount = 0;
652
- page.inFlightCount = 0;
653
- }
1190
+ const discriminant = (1 - mapping.strength) ** 2 + 4 * mapping.strength * sourceRadius * sourceRadius / mapping.radiusSquared;
1191
+ const displayRadius = 2 * sourceRadius / (1 - mapping.strength + Math.sqrt(Math.max(0, discriminant)));
1192
+ const scale = displayRadius / sourceRadius;
1193
+ return normalizeComputedUv(
1194
+ out,
1195
+ mapping.centerX + (ux - mapping.centerX) * scale,
1196
+ mapping.centerY + (uy - mapping.centerY) * scale
1197
+ );
1198
+ }
1199
+ function validPixel(point) {
1200
+ return Number.isFinite(point.x) && Number.isFinite(point.y);
1201
+ }
1202
+ function mapDisplayToScene(out, mapping, displayX, displayY) {
1203
+ if (!Number.isFinite(displayX) || !Number.isFinite(displayY)) return false;
1204
+ if (displayX < 0 || displayX > mapping.width || displayY < 0 || displayY > mapping.height) {
1205
+ return false;
654
1206
  }
655
- releasePage(pageIndex) {
656
- const page = this.pages[pageIndex];
657
- if (page !== void 0 && page.pendingCount === 0 && page.inFlightCount === 0) {
658
- page.nextQueryIndex = 0;
659
- }
1207
+ const scene = { x: 0, y: 0 };
1208
+ if (!mapDisplayUvToSceneUv(scene, mapping, displayX / mapping.width, displayY / mapping.height)) {
1209
+ return false;
660
1210
  }
661
- };
1211
+ out.x = scene.x * mapping.width;
1212
+ out.y = scene.y * mapping.height;
1213
+ return validPixel(out);
1214
+ }
1215
+ function mapSceneToDisplay(out, mapping, sceneX, sceneY) {
1216
+ if (!Number.isFinite(sceneX) || !Number.isFinite(sceneY)) return false;
1217
+ if (sceneX < 0 || sceneX > mapping.width || sceneY < 0 || sceneY > mapping.height) {
1218
+ return false;
1219
+ }
1220
+ const display = { x: 0, y: 0 };
1221
+ if (!mapSceneUvToDisplayUv(display, mapping, sceneX / mapping.width, sceneY / mapping.height)) {
1222
+ return false;
1223
+ }
1224
+ out.x = display.x * mapping.width;
1225
+ out.y = display.y * mapping.height;
1226
+ return validPixel(out);
1227
+ }
662
1228
 
663
- // src/scene/visibility/occlusion-pass.ts
664
- function createOcclusionQueryResources(device, pageIndex) {
665
- const querySet = device.createQuerySet({
666
- label: `occlusion-page-${pageIndex}`,
667
- type: "occlusion",
668
- count: OCCLUSION_QUERY_PAGE_INDEX_LIMIT
669
- });
670
- if (!querySet.ok) return querySet;
671
- const resolveBuffer = device.createBuffer({
672
- label: `occlusion-page-${pageIndex}-resolve`,
673
- size: OCCLUSION_QUERY_PAGE_BYTES,
674
- usage: GPU_BUFFER_USAGE_QUERY_RESOLVE | GPU_BUFFER_USAGE_COPY_SRC
675
- });
676
- if (!resolveBuffer.ok) {
677
- device.destroyQuerySet(querySet.value);
678
- return resolveBuffer;
679
- }
680
- const stagingBuffer = device.createBuffer({
681
- label: `occlusion-page-${pageIndex}-staging`,
682
- size: OCCLUSION_QUERY_PAGE_BYTES,
683
- usage: GPU_BUFFER_USAGE_MAP_READ | GPU_BUFFER_USAGE_COPY_DST
684
- });
685
- if (!stagingBuffer.ok) {
686
- device.destroyBuffer(resolveBuffer.value);
687
- device.destroyQuerySet(querySet.value);
688
- return stagingBuffer;
689
- }
690
- return ok$1({
691
- pageIndex,
692
- querySet: querySet.value,
693
- resolveBuffer: resolveBuffer.value,
694
- stagingBuffer: stagingBuffer.value
695
- });
1229
+ // src/errors/cloud.ts
1230
+ var CloudLayerInvalidParameterError = class extends Error {
1231
+ code = "cloud-layer-invalid-parameter";
1232
+ expected;
1233
+ hint;
1234
+ detail;
1235
+ constructor(field, value, expected) {
1236
+ super(`CloudLayer.${field} is invalid`);
1237
+ this.name = "CloudLayerInvalidParameterError";
1238
+ this.expected = `CloudLayer.${field} must be ${expected}`;
1239
+ this.hint = `set CloudLayer.${field} to ${expected}`;
1240
+ this.detail = { field, value, expected };
1241
+ }
1242
+ };
1243
+ var CloudLayerOwnerConflictError = class extends Error {
1244
+ code = "cloud-layer-owner-conflict";
1245
+ expected = "at most one CloudLayer owns a World frame";
1246
+ hint = "remove additional CloudLayer owners before extraction";
1247
+ detail;
1248
+ constructor(count) {
1249
+ super(`CloudLayer owner cardinality is ${count}`);
1250
+ this.name = "CloudLayerOwnerConflictError";
1251
+ this.detail = { count };
1252
+ }
1253
+ };
1254
+ var CloudLayerCacheInvalidError = class extends Error {
1255
+ code = "cloud-layer-cache-invalid";
1256
+ expected = "the density cache payload matches its reconstructible source";
1257
+ hint = "discard the derived cache and rebuild it from CloudLayer source facts";
1258
+ detail;
1259
+ constructor(sourceKey, reason) {
1260
+ super(`CloudLayer cache is invalid: ${reason}`);
1261
+ this.name = "CloudLayerCacheInvalidError";
1262
+ this.detail = { sourceKey, reason };
1263
+ }
1264
+ };
1265
+ var CloudLayerCapabilityMissingError = class extends Error {
1266
+ code = "cloud-layer-capability-missing";
1267
+ expected = "the selected backend exposes the capability required by the cloud lane";
1268
+ hint = "inspect the capability report, disable the cloud lane on this backend, or retry on a backend with the required capability";
1269
+ detail;
1270
+ constructor(capability) {
1271
+ super(`CloudLayer capability is unavailable: ${capability}`);
1272
+ this.name = "CloudLayerCapabilityMissingError";
1273
+ this.detail = { capability };
1274
+ }
1275
+ };
1276
+ var CloudLayerResourceFailureError = class extends Error {
1277
+ code = "cloud-layer-resource-failed";
1278
+ expected = "cloud resources are created, submitted and retired as one generation";
1279
+ hint = "keep the last-known-good cloud generation and retry after recovery";
1280
+ detail;
1281
+ constructor(detail) {
1282
+ super(`CloudLayer ${detail.stage} resource failed at generation ${detail.generation}`);
1283
+ this.name = "CloudLayerResourceFailureError";
1284
+ this.detail = detail;
1285
+ }
1286
+ };
1287
+ var CLOUD_QUALITY_PROFILES = Object.freeze({
1288
+ low: Object.freeze({
1289
+ // Preserve low-profile ray counts while keeping separate puffs from
1290
+ // collapsing into one bilinear cache smear.
1291
+ cacheResolution: 32,
1292
+ viewSteps: 40,
1293
+ shadowSteps: 12,
1294
+ historyWeight: 0.82,
1295
+ viewDistance: 1600
1296
+ }),
1297
+ medium: Object.freeze({
1298
+ cacheResolution: 40,
1299
+ viewSteps: 64,
1300
+ shadowSteps: 20,
1301
+ historyWeight: 0.9,
1302
+ viewDistance: 2400
1303
+ }),
1304
+ high: Object.freeze({
1305
+ cacheResolution: 64,
1306
+ viewSteps: 64,
1307
+ shadowSteps: 32,
1308
+ historyWeight: 0.94,
1309
+ viewDistance: 4e3
1310
+ })
1311
+ });
1312
+ function cloudShadowResolutionForQuality(qualityValue) {
1313
+ return CLOUD_QUALITY_PROFILES[qualityValue].cacheResolution * 4;
1314
+ }
1315
+ function cloudViewDistanceForQuality(qualityValue) {
1316
+ return CLOUD_QUALITY_PROFILES[qualityValue].viewDistance;
1317
+ }
1318
+ var CLOUD_EXTINCTION_COEFFICIENT = 0.03;
1319
+ var DEFAULT_CLOUD_LAYER = Object.freeze({
1320
+ seed: 1337,
1321
+ baseHeight: 120,
1322
+ thickness: 80,
1323
+ scale: 4e-3,
1324
+ coverage: 0.48,
1325
+ density: 1,
1326
+ wind: Object.freeze([8, 0, 2]),
1327
+ quality: "medium",
1328
+ shadowRange: 512
1329
+ });
1330
+ function invalid3(field, value, expected) {
1331
+ return err(new CloudLayerInvalidParameterError(field, value, expected));
696
1332
  }
697
- function recordOcclusionResolve(encoder, resources, firstQuery, queryCount) {
698
- if (!Number.isSafeInteger(firstQuery) || !Number.isSafeInteger(queryCount) || firstQuery < 0 || queryCount <= 0 || firstQuery + queryCount > OCCLUSION_QUERY_PAGE_INDEX_LIMIT) {
699
- return err$1(
700
- new RhiError({
701
- code: "webgpu-runtime-error",
702
- expected: "query range stays within one 4096-index page",
703
- hint: "allocate a bounded page before recording occlusion resolve"
704
- })
705
- );
1333
+ function finiteRange(field, value, min, max = Number.POSITIVE_INFINITY) {
1334
+ if (typeof value !== "number" || !Number.isFinite(value) || value < min || value > max) {
1335
+ const expected = Number.isFinite(max) ? `finite and in [${min}, ${max}]` : `finite and >= ${min}`;
1336
+ return invalid3(field, value, expected);
706
1337
  }
707
- const resolved = encoder.resolveQuerySet(
708
- resources.querySet,
709
- firstQuery,
710
- queryCount,
711
- resources.resolveBuffer,
712
- 0
1338
+ return ok(value);
1339
+ }
1340
+ function quality(value) {
1341
+ if (typeof value === "string" && (value === "low" || value === "medium" || value === "high")) {
1342
+ return ok(value);
1343
+ }
1344
+ if (typeof value === "number") {
1345
+ const resolved = cloudQualityFromF32(value);
1346
+ if (resolved !== void 0) return ok(resolved);
1347
+ }
1348
+ return invalid3(
1349
+ "quality",
1350
+ value,
1351
+ `one of low, medium, high (codes ${CloudQualityValue.low}, ${CloudQualityValue.medium}, ${CloudQualityValue.high})`
713
1352
  );
714
- if (!resolved.ok) return resolved;
715
- encoder.copyBufferToBuffer(resources.resolveBuffer, resources.stagingBuffer, queryCount * 8);
716
- return ok$1(void 0);
1353
+ }
1354
+ function wind(value) {
1355
+ if (value === void 0 || value === null || typeof value !== "object") {
1356
+ return invalid3("wind", value, "three finite world-space components");
1357
+ }
1358
+ const source = value;
1359
+ if (source.length !== 3) return invalid3("wind", value, "three finite world-space components");
1360
+ const result = [
1361
+ source[0] ?? Number.NaN,
1362
+ source[1] ?? Number.NaN,
1363
+ source[2] ?? Number.NaN
1364
+ ];
1365
+ if (result.some((entry) => !Number.isFinite(entry) || Math.abs(entry) > 1e3)) {
1366
+ return invalid3("wind", value, "three finite components in [-1000, 1000]");
1367
+ }
1368
+ return ok(Object.freeze(result));
1369
+ }
1370
+ function validateCloudLayer(input) {
1371
+ const value = input ?? {};
1372
+ const seed = value.seed ?? DEFAULT_CLOUD_LAYER.seed;
1373
+ if (typeof seed !== "number" || !Number.isInteger(seed) || seed < 0 || seed > 4294967295) {
1374
+ return invalid3("seed", seed, "an integer in [0, 4294967295]");
1375
+ }
1376
+ const baseHeight = finiteRange(
1377
+ "baseHeight",
1378
+ value.baseHeight ?? DEFAULT_CLOUD_LAYER.baseHeight,
1379
+ -1e5,
1380
+ 1e6
1381
+ );
1382
+ if (!baseHeight.ok) return baseHeight;
1383
+ const thickness = finiteRange(
1384
+ "thickness",
1385
+ value.thickness ?? DEFAULT_CLOUD_LAYER.thickness,
1386
+ 1e-3,
1387
+ 1e6
1388
+ );
1389
+ if (!thickness.ok) return thickness;
1390
+ const scale = finiteRange("scale", value.scale ?? DEFAULT_CLOUD_LAYER.scale, 1e-6, 10);
1391
+ if (!scale.ok) return scale;
1392
+ const coverage = finiteRange("coverage", value.coverage ?? DEFAULT_CLOUD_LAYER.coverage, 0, 1);
1393
+ if (!coverage.ok) return coverage;
1394
+ const density = finiteRange("density", value.density ?? DEFAULT_CLOUD_LAYER.density, 0, 32);
1395
+ if (!density.ok) return density;
1396
+ const resolvedWind = wind(value.wind ?? DEFAULT_CLOUD_LAYER.wind);
1397
+ if (!resolvedWind.ok) return resolvedWind;
1398
+ const resolvedQuality = quality(value.quality ?? DEFAULT_CLOUD_LAYER.quality);
1399
+ if (!resolvedQuality.ok) return resolvedQuality;
1400
+ const shadowRange = finiteRange(
1401
+ "shadowRange",
1402
+ value.shadowRange ?? DEFAULT_CLOUD_LAYER.shadowRange,
1403
+ 1e-3,
1404
+ 1e7
1405
+ );
1406
+ if (!shadowRange.ok) return shadowRange;
1407
+ return ok(
1408
+ Object.freeze({
1409
+ seed,
1410
+ baseHeight: baseHeight.value,
1411
+ thickness: thickness.value,
1412
+ scale: scale.value,
1413
+ coverage: coverage.value,
1414
+ density: density.value,
1415
+ wind: resolvedWind.value,
1416
+ quality: resolvedQuality.value,
1417
+ shadowRange: shadowRange.value
1418
+ })
1419
+ );
1420
+ }
1421
+ function cloudLayerSourceKey(params) {
1422
+ return [
1423
+ "cloud-layer-v1",
1424
+ params.seed,
1425
+ params.baseHeight,
1426
+ params.thickness,
1427
+ params.scale,
1428
+ params.coverage,
1429
+ params.density,
1430
+ params.wind[0],
1431
+ params.wind[1],
1432
+ params.wind[2],
1433
+ params.quality,
1434
+ params.shadowRange
1435
+ ].join(":");
1436
+ }
1437
+ function cloudLayerFormationKey(params) {
1438
+ return [
1439
+ "cloud-formation-v5",
1440
+ params.seed,
1441
+ params.baseHeight,
1442
+ params.thickness,
1443
+ params.scale,
1444
+ params.quality
1445
+ ].join(":");
1446
+ }
1447
+ var MAX_VOLUMETRIC_FOG_OWNERS = 8;
1448
+ var VolumetricFogSamplingValue = { noise: 0, density: 1 };
1449
+ var VolumetricFog = defineComponent("VolumetricFog", {
1450
+ light: { type: "entity" },
1451
+ spotLight: { type: "entity" },
1452
+ density: { type: "shared<TextureAsset>", simulationTransient: true },
1453
+ boundsMin: { type: "array<f32, 3>" },
1454
+ boundsMax: { type: "array<f32, 3>" },
1455
+ extinction: { type: "array<f32, 3>" },
1456
+ albedo: { type: "array<f32, 3>" },
1457
+ emission: { type: "array<f32, 3>" },
1458
+ anisotropy: { type: "f32", default: 0 },
1459
+ maxDistance: { type: "f32", default: 100 },
1460
+ sampling: { type: "enum", labels: VolumetricFogSamplingValue, default: 0 }
1461
+ });
1462
+ function finiteVector(value) {
1463
+ return value.every(Number.isFinite);
1464
+ }
1465
+ function validBounds(bounds) {
1466
+ if (!finiteVector(bounds.min) || !finiteVector(bounds.max)) return false;
1467
+ return bounds.max.every((value, index) => value > (bounds.min[index] ?? value));
1468
+ }
1469
+ function validRange(value, min, max = Infinity) {
1470
+ return value.every((entry) => Number.isFinite(entry) && entry >= min && entry <= max);
1471
+ }
1472
+ function validateVolumetricFog(input) {
1473
+ if (input.density.shape.viewDimension !== "3d" || input.density.colorSpace === "srgb") {
1474
+ return err(
1475
+ new VolumeDensityShapeMismatchError(input.density.guid, input.density.shape.viewDimension)
1476
+ );
1477
+ }
1478
+ if (!validBounds(input.bounds)) return err(new VolumeInvalidBoundsError(input.bounds));
1479
+ if (!validRange(input.extinction, 0) || !validRange(input.albedo, 0, 1) || !validRange(input.emission, 0) || !Number.isFinite(input.anisotropy) || input.anisotropy <= -1 || input.anisotropy >= 1 || !Number.isFinite(input.maxDistance) || input.maxDistance <= 0 || input.sampling !== void 0 && input.sampling !== "noise" && input.sampling !== "density") {
1480
+ return err(new VolumeInvalidParametersError(input));
1481
+ }
1482
+ return ok(input);
717
1483
  }
718
1484
 
719
- // src/scene/visibility/types.ts
720
- function viewKeyId(key) {
721
- return `${key.attachmentId}:${key.cameraEntity}:${key.viewRole}:${key.viewGeneration}`;
1485
+ // src/environment/signature.ts
1486
+ function sorted(items) {
1487
+ return [...items].sort((left, right) => left.entityKey - right.entityKey);
1488
+ }
1489
+ function environmentSignature(input) {
1490
+ return JSON.stringify({
1491
+ environments: sorted(input.environments),
1492
+ fogs: sorted(input.fogs),
1493
+ suns: sorted(input.suns)
1494
+ });
722
1495
  }
723
- function primitiveKeyId(key) {
724
- return `${key.attachmentId}:${key.worldGeneration}:${key.primitiveSlot}:${key.slotGeneration}`;
1496
+ function environmentOnlySignature(input) {
1497
+ return JSON.stringify({
1498
+ environments: sorted(input.environments),
1499
+ suns: sorted(input.suns)
1500
+ });
725
1501
  }
726
- function viewKey(input) {
727
- return Object.freeze({ ...input });
1502
+ function fogOnlySignature(input) {
1503
+ return JSON.stringify({ fogs: sorted(input.fogs) });
728
1504
  }
729
- function primitiveKey(input) {
730
- return Object.freeze({ ...input });
1505
+ function environmentFactSignatures(input) {
1506
+ return {
1507
+ environmentSignature: environmentOnlySignature(input),
1508
+ fogSignature: fogOnlySignature(input),
1509
+ signature: environmentSignature(input)
1510
+ };
731
1511
  }
732
1512
 
733
- // src/scene/visibility/occlusion-runtime.ts
734
- var PROXY_VERTEX_FLOATS_PER_CANDIDATE = 36 * 4;
735
- var PROXY_GEOMETRY_BYTES = OCCLUSION_QUERY_PAGE_INDEX_LIMIT * PROXY_VERTEX_FLOATS_PER_CANDIDATE * 4;
736
- var runtimeTestHooks;
737
- function setOcclusionRuntimeTestHooks(hooks) {
738
- runtimeTestHooks = hooks;
739
- }
740
- var OcclusionRenderRuntime = class {
741
- constructor(device, facets, shaderModuleFactory, budget = facets.visibilityBudget()) {
742
- this.device = device;
743
- this.facets = facets;
744
- this.shaderModuleFactory = shaderModuleFactory;
745
- this.budget = budget;
746
- const runtime = this;
747
- this.proxyScratch = new Float32Array(
748
- Math.min(this.budget.effectiveQueryBudget, OCCLUSION_QUERY_PAGE_INDEX_LIMIT) * PROXY_VERTEX_FLOATS_PER_CANDIDATE
749
- );
750
- this.projection = {
751
- get querySet() {
752
- return runtime.pendingFrame?.resources.querySet;
753
- },
754
- get queryIndex() {
755
- return runtime.pendingFrame?.reservations[0]?.queryIndex ?? 0;
756
- },
757
- get queryCount() {
758
- return runtime.pendingFrame?.reservations.length ?? 0;
759
- },
760
- get sampleCount() {
761
- return runtime.pendingFrame?.sampleCount ?? 1;
762
- },
763
- get pageIndex() {
764
- return runtime.pendingFrame?.reservations[0]?.pageIndex ?? 0;
765
- },
766
- get reservationId() {
767
- return runtime.pendingFrame?.reservations[0]?.reservationId ?? 0;
768
- },
769
- encodeProxyBounds: (pass) => {
770
- const pending = runtime.pendingFrame;
771
- return pending === void 0 ? err$1(runtimeError("the current RenderSystem frame owns the occlusion candidate")) : runtime.encodeProxyBounds(pass, pending);
772
- },
773
- beginCandidate: (pass) => {
774
- const pending = runtime.pendingFrame;
775
- if (pending === void 0 || pending.endedCount >= pending.reservations.length) {
776
- return err$1(runtimeError("the current RenderSystem frame owns the occlusion candidate"));
777
- }
778
- return pass.beginOcclusionQuery(pending.reservations[pending.endedCount]?.queryIndex ?? 0);
779
- },
780
- endCandidate: (pass) => {
781
- const pending = runtime.pendingFrame;
782
- if (pending === void 0 || pending.endedCount >= pending.reservations.length) {
783
- return err$1(runtimeError("the current RenderSystem frame owns the occlusion candidate"));
784
- }
785
- const ended = pass.endOcclusionQuery();
786
- if (ended.ok) pending.endedCount += 1;
787
- return ended;
788
- },
789
- recordResolve: (encoder) => {
790
- const pending = runtime.pendingFrame;
791
- if (pending === void 0 || pending.endedCount !== pending.reservations.length) {
792
- return err$1(runtimeError("the real geometry pass must end its occlusion query first"));
793
- }
794
- return recordOcclusionResolve(
795
- encoder,
796
- pending.resources,
797
- pending.reservations[0]?.queryIndex ?? 0,
798
- pending.reservations.length
799
- );
800
- },
801
- commit: (submitted, submissionGeneration, profilePhase) => {
802
- const pending = runtime.pendingFrame;
803
- if (pending !== void 0)
804
- runtime.commit(pending, submitted, submissionGeneration, profilePhase);
805
- }
1513
+ // src/extract/environment.ts
1514
+ var ATMOSPHERE_PARAMETER_RANGES = Object.freeze({
1515
+ turbidity: Object.freeze({ min: 1, max: 20 }),
1516
+ rayleigh: Object.freeze({ min: 0, max: Number.POSITIVE_INFINITY }),
1517
+ mieCoefficient: Object.freeze({ min: 0, max: Number.POSITIVE_INFINITY }),
1518
+ mieDirectionalG: Object.freeze({ min: 0, max: 0.999 }),
1519
+ sunAngularRadius: Object.freeze({ min: 0, max: Number.POSITIVE_INFINITY }),
1520
+ circumsolarStrength: Object.freeze({ min: 0, max: 4 }),
1521
+ circumsolarWidth: Object.freeze({ min: 0.25, max: 4 })
1522
+ });
1523
+ function validateFogParameters(candidate) {
1524
+ if (candidate.color.length !== 3) {
1525
+ return {
1526
+ field: "color",
1527
+ value: candidate.color.length,
1528
+ expected: "exactly three finite channels in [0, 1]"
806
1529
  };
807
1530
  }
808
- device;
809
- facets;
810
- shaderModuleFactory;
811
- budget;
812
- pool = new OcclusionQueryPool();
813
- resources = /* @__PURE__ */ new Map();
814
- activeCompletions = /* @__PURE__ */ new Map();
815
- retiredResources = /* @__PURE__ */ new Set();
816
- /** Candidate descriptors are rebuilt only when the extracted frame changes. */
817
- candidateSource;
818
- validCandidates = [];
819
- candidateByKey = /* @__PURE__ */ new Map();
820
- lastPrepareUnavailable = false;
821
- pendingFrame;
822
- disposed = false;
823
- proxyBuffer;
824
- proxyScratch;
825
- proxyPipeline;
826
- proxyPipelineSampleCount;
827
- queryLatencySamplesUs = [];
828
- lastQueryLatencyUs = 0;
829
- projection;
830
- prepare(candidate) {
831
- return this.prepareBatch([candidate], 1, false);
832
- }
833
- prepareBatch(candidates, sampleCount = 1, useReadyQueue = true) {
834
- this.lastPrepareUnavailable = false;
835
- if (this.disposed || candidates.length === 0) return void 0;
836
- if (this.pendingFrame !== void 0) {
837
- this.lastPrepareUnavailable = true;
838
- return void 0;
1531
+ for (let index = 0; index < candidate.color.length; index += 1) {
1532
+ const value = candidate.color[index];
1533
+ if (value === void 0 || !Number.isFinite(value) || value < 0 || value > 1) {
1534
+ return {
1535
+ field: `color[${index}]`,
1536
+ value: value ?? Number.NaN,
1537
+ expected: "a finite number in [0, 1]"
1538
+ };
839
1539
  }
840
- if (this.candidateSource !== candidates) {
841
- this.candidateSource = candidates;
842
- this.candidateByKey.clear();
843
- const valid2 = [];
844
- for (const candidate of candidates) {
845
- this.candidateByKey.set(candidateKey(candidate.view, candidate.primitive), candidate);
846
- if (finiteBounds(candidate.bounds)) valid2.push(candidate);
847
- }
848
- this.validCandidates = valid2;
1540
+ }
1541
+ if (!Number.isFinite(candidate.density) || candidate.density < 0) {
1542
+ return { field: "density", value: candidate.density, expected: "a finite number >= 0" };
1543
+ }
1544
+ if (!Number.isFinite(candidate.heightFalloff) || candidate.heightFalloff < 0) {
1545
+ return {
1546
+ field: "heightFalloff",
1547
+ value: candidate.heightFalloff,
1548
+ expected: "a finite number >= 0"
1549
+ };
1550
+ }
1551
+ if (!Number.isFinite(candidate.maxOpacity) || candidate.maxOpacity < 0 || candidate.maxOpacity > 1) {
1552
+ return {
1553
+ field: "maxOpacity",
1554
+ value: candidate.maxOpacity,
1555
+ expected: "a finite number in [0, 1]"
1556
+ };
1557
+ }
1558
+ return void 0;
1559
+ }
1560
+ function validateAtmosphereParameters(parameters) {
1561
+ for (const field of Object.keys(ATMOSPHERE_PARAMETER_RANGES)) {
1562
+ const value = parameters[field];
1563
+ const range = ATMOSPHERE_PARAMETER_RANGES[field];
1564
+ if (!Number.isFinite(value) || value < range.min || value > range.max) {
1565
+ return new AtmosphereInvalidParameterError(field, value, range);
849
1566
  }
850
- const valid = this.validCandidates;
851
- if (valid.length === 0) return void 0;
852
- let selectedCandidates;
853
- if (!useReadyQueue) {
854
- selectedCandidates = valid.slice(0, this.budget.effectiveQueryBudget);
855
- } else {
856
- const firstView = valid[0]?.view;
857
- if (firstView === void 0) return void 0;
858
- selectedCandidates = [];
859
- for (const entry of this.facets.dequeueQueryCandidates(
860
- firstView,
861
- this.budget.effectiveQueryBudget
862
- )) {
863
- const candidate = this.candidateByKey.get(candidateKey(entry.view, entry.primitive));
864
- if (candidate === void 0 || !finiteBounds(candidate.bounds)) {
865
- this.facets.requeueQueryCandidate(entry.view, entry.primitive);
866
- continue;
867
- }
868
- selectedCandidates.push(candidate);
869
- }
870
- if (selectedCandidates.length === 0) return void 0;
1567
+ }
1568
+ return void 0;
1569
+ }
1570
+
1571
+ // src/environment/frame.ts
1572
+ var SKYLIGHT_RECOVERY_FALLBACK = Object.freeze({
1573
+ kind: "skylight",
1574
+ active: false
1575
+ });
1576
+ var EnvironmentSelectionParameterError = class extends Error {
1577
+ code = "environment-selection-invalid";
1578
+ expected;
1579
+ hint;
1580
+ detail;
1581
+ constructor(field, value, expected) {
1582
+ super(`environment selection field ${field} is invalid`);
1583
+ this.name = "EnvironmentSelectionParameterError";
1584
+ this.expected = expected;
1585
+ this.hint = `set ${field} to a finite value satisfying ${expected}`;
1586
+ this.detail = { field, value };
1587
+ }
1588
+ };
1589
+ var SunCardinalityError = class extends Error {
1590
+ code = "sun-cardinality";
1591
+ expected = "atmosphere has exactly one sun owner";
1592
+ hint = "add exactly one DirectionalLight sun for the atmosphere owner";
1593
+ detail;
1594
+ constructor(value) {
1595
+ super(`atmosphere sun cardinality is ${value}`);
1596
+ this.name = "SunCardinalityError";
1597
+ this.detail = { field: "sun", value };
1598
+ }
1599
+ };
1600
+ function freezeCandidate(candidate) {
1601
+ return Object.freeze(candidate);
1602
+ }
1603
+ function freezeFog(candidate) {
1604
+ return Object.freeze({
1605
+ ...candidate,
1606
+ color: Object.freeze([...candidate.color])
1607
+ });
1608
+ }
1609
+ function selectEnvironment(input) {
1610
+ if (input.environments.length > 1) {
1611
+ return err(
1612
+ new EnvironmentSourceConflictError(
1613
+ [...input.environments].sort((left, right) => left.entityKey - right.entityKey).map(({ kind, entityKey, sourceKey }) => ({ kind, entityKey, sourceKey }))
1614
+ )
1615
+ );
1616
+ }
1617
+ if (input.fogs.length > 1) return err(new FogCardinalityError(input.fogs.length));
1618
+ const selectedFog = input.fogs[0];
1619
+ if (selectedFog !== void 0) {
1620
+ const issue = validateFogParameters(selectedFog);
1621
+ if (issue !== void 0) {
1622
+ return err(new EnvironmentSelectionParameterError(issue.field, issue.value, issue.expected));
871
1623
  }
872
- const reservations = [];
873
- let selectedCount = 0;
874
- while (selectedCount < selectedCandidates.length) {
875
- const candidate = selectedCandidates[selectedCount];
876
- if (candidate === void 0) break;
877
- const reservation = this.pool.reserve({
878
- viewKey: viewKeyId(candidate.view),
879
- attachmentId: candidate.view.attachmentId,
880
- deviceGeneration: candidate.deviceGeneration,
881
- worldGeneration: candidate.primitive.worldGeneration,
882
- primitiveSlot: candidate.primitive.primitiveSlot,
883
- slotGeneration: candidate.primitive.slotGeneration
884
- });
885
- if (reservation === void 0) {
886
- this.lastPrepareUnavailable = true;
887
- break;
888
- }
889
- if (reservations.length > 0 && reservation.pageIndex !== reservations[0]?.pageIndex) {
890
- this.pool.cancel(reservation);
891
- this.lastPrepareUnavailable = true;
892
- break;
893
- }
894
- reservations.push(reservation);
895
- selectedCount += 1;
896
- if (reservations.length >= this.budget.effectiveQueryBudget) break;
1624
+ }
1625
+ const selected = input.environments[0];
1626
+ if (selected?.kind === "atmosphere") {
1627
+ const invalid5 = validateAtmosphereParameters(selected.atmosphere);
1628
+ if (invalid5 !== void 0) return err(invalid5);
1629
+ }
1630
+ if (selected?.kind === "atmosphere" && input.suns.length !== 1) {
1631
+ return err(new SunCardinalityError(input.suns.length));
1632
+ }
1633
+ const source = selected === void 0 ? { kind: "none" } : selected.kind === "atmosphere" ? {
1634
+ kind: "atmosphere",
1635
+ entityKey: selected.entityKey,
1636
+ sourceKey: selected.sourceKey,
1637
+ atmosphere: Object.freeze({ ...selected.atmosphere })
1638
+ } : {
1639
+ kind: "image",
1640
+ entityKey: selected.entityKey,
1641
+ sourceKey: selected.sourceKey
1642
+ };
1643
+ const sun = input.suns[0] === void 0 ? void 0 : freezeCandidate({ ...input.suns[0] });
1644
+ const fog = selectedFog === void 0 ? void 0 : freezeFog(selectedFog);
1645
+ const signatureInput = {
1646
+ environments: selected === void 0 ? [] : [selected],
1647
+ fogs: selectedFog === void 0 ? [] : [selectedFog],
1648
+ suns: input.suns
1649
+ };
1650
+ const signatures = environmentFactSignatures(signatureInput);
1651
+ const result = {
1652
+ source: Object.freeze(source),
1653
+ fog,
1654
+ ...signatures,
1655
+ revision: 0,
1656
+ sun
1657
+ };
1658
+ Object.defineProperty(result, "lane", { value: input.lane, enumerable: false });
1659
+ return ok(Object.freeze(result));
1660
+ }
1661
+ var DEPTH_OF_FIELD_PARAMS_BYTE_SIZE = 64;
1662
+ var MIN_F_STOP = Math.fround(0.7);
1663
+ var MAX_F_STOP = Math.fround(32);
1664
+ var MAX_LINEAR_DEPTH_RAW = Math.fround(1 - 2 ** -24);
1665
+ var DEFAULT_DEPTH_OF_FIELD_PARAMS = Object.freeze({
1666
+ focusDistance: 8,
1667
+ fStop: 2.8,
1668
+ sensorHeight: 0.024,
1669
+ maxRadiusPixels: 16,
1670
+ quality: "medium",
1671
+ blurSide: "both",
1672
+ focalLength: 0
1673
+ });
1674
+ function quantizeF32(value) {
1675
+ return Math.fround(value);
1676
+ }
1677
+ var DepthOfFieldValidationError = class extends Error {
1678
+ code;
1679
+ expected;
1680
+ hint;
1681
+ detail;
1682
+ constructor(code, detail, expected, hint) {
1683
+ super(`${code}: ${expected}`);
1684
+ this.name = "DepthOfFieldValidationError";
1685
+ this.code = code;
1686
+ this.expected = expected;
1687
+ this.hint = hint;
1688
+ this.detail = detail;
1689
+ }
1690
+ };
1691
+ function depthOfFieldRequestFailure(error) {
1692
+ return Object.freeze({
1693
+ code: error.code,
1694
+ detail: error.detail,
1695
+ expected: error.expected,
1696
+ hint: error.hint
1697
+ });
1698
+ }
1699
+ function resolveDepthOfFieldFrameParams(requested, requestFailure, accepted) {
1700
+ if (requested !== void 0) return requested;
1701
+ if (requestFailure === void 0 || requestFailure.code === "depth-of-field-orthographic-unsupported") {
1702
+ return void 0;
1703
+ }
1704
+ return accepted;
1705
+ }
1706
+ function invalid4(field, value, expected, bound) {
1707
+ return new DepthOfFieldValidationError(
1708
+ "depth-of-field-invalid-params",
1709
+ { field, value, expected, ...bound === void 0 ? {} : { bound } },
1710
+ expected,
1711
+ `set ${field} to ${expected}`
1712
+ );
1713
+ }
1714
+ function focalLength(sensorHeight, fov) {
1715
+ return sensorHeight / (2 * Math.tan(fov / 2));
1716
+ }
1717
+ function opticalCocScale(params) {
1718
+ return params.focalLength / params.sensorHeight / (2 * params.fStop) * (params.focalLength / (params.focusDistance - params.focalLength));
1719
+ }
1720
+ function frameCocCoefficient(params, outputHeight) {
1721
+ return quantizeF32(quantizeF32(outputHeight) * opticalCocScale(params));
1722
+ }
1723
+ function resolveDepthOfFieldFrameValues(params, input) {
1724
+ const focusDistance = quantizeF32(params.focusDistance);
1725
+ const fStop = quantizeF32(params.fStop);
1726
+ const sensorHeight = quantizeF32(params.sensorHeight);
1727
+ const focal = quantizeF32(params.focalLength);
1728
+ const maxRadiusPixels = quantizeF32(params.maxRadiusPixels);
1729
+ const outputHeight = quantizeF32(input.outputHeight);
1730
+ const near = quantizeF32(input.near);
1731
+ const far = quantizeF32(input.far);
1732
+ const finitePositiveFields = [
1733
+ ["focusDistance", focusDistance, params.focusDistance],
1734
+ ["sensorHeight", sensorHeight, params.sensorHeight],
1735
+ ["focalLength", focal, params.focalLength],
1736
+ ["near", near, input.near],
1737
+ ["far", far, input.far]
1738
+ ];
1739
+ for (const [field, value, raw] of finitePositiveFields) {
1740
+ if (!Number.isFinite(value) || value <= 0) {
1741
+ return err(invalid4(field, raw, "a finite positive f32 value"));
1742
+ }
1743
+ }
1744
+ if (!Number.isFinite(fStop) || fStop < MIN_F_STOP || fStop > MAX_F_STOP) {
1745
+ return err(
1746
+ invalid4("fStop", params.fStop, "a finite f32 value in [0.7, 32]", [MIN_F_STOP, MAX_F_STOP])
1747
+ );
1748
+ }
1749
+ if (!Number.isFinite(maxRadiusPixels) || maxRadiusPixels < 0 || maxRadiusPixels > 32) {
1750
+ return err(
1751
+ invalid4("maxRadiusPixels", params.maxRadiusPixels, "a finite f32 value in [0, 32]", [0, 32])
1752
+ );
1753
+ }
1754
+ if (!Number.isFinite(outputHeight) || outputHeight <= 0) {
1755
+ return err(invalid4("outputHeight", input.outputHeight, "a finite positive f32 value"));
1756
+ }
1757
+ if (far <= near) {
1758
+ return err(invalid4("near/far", [input.near, input.far], "finite f32 near > 0 and far > near"));
1759
+ }
1760
+ if (focusDistance <= focal) {
1761
+ return err(invalid4("focusDistance", params.focusDistance, "greater than focalLength in f32"));
1762
+ }
1763
+ const depthRatio = quantizeF32(near / far);
1764
+ if (!Number.isFinite(depthRatio) || depthRatio <= 0) {
1765
+ return err(invalid4("near/far ratio", depthRatio, "a finite positive f32 value"));
1766
+ }
1767
+ for (const raw of [quantizeF32(2 ** -149), 0.5, MAX_LINEAR_DEPTH_RAW]) {
1768
+ const oneMinusRaw = quantizeF32(1 - raw);
1769
+ const weightedRatio = quantizeF32(raw * depthRatio);
1770
+ const denominator = quantizeF32(oneMinusRaw + weightedRatio);
1771
+ const distance = quantizeF32(near / denominator);
1772
+ if (!Number.isFinite(oneMinusRaw) || oneMinusRaw <= 0 || !Number.isFinite(weightedRatio) || !Number.isFinite(denominator) || denominator <= 0 || !Number.isFinite(distance) || distance <= 0) {
1773
+ return err(invalid4("linearDepth", distance, "finite positive f32 reconstruction"));
1774
+ }
1775
+ }
1776
+ const cocCoefficient = frameCocCoefficient(
1777
+ {
1778
+ focusDistance,
1779
+ fStop,
1780
+ sensorHeight,
1781
+ focalLength: focal
1782
+ },
1783
+ outputHeight
1784
+ );
1785
+ if (!Number.isFinite(cocCoefficient) || cocCoefficient <= 0) {
1786
+ return err(
1787
+ invalid4("cocCoefficient", cocCoefficient, "a finite positive value representable as f32")
1788
+ );
1789
+ }
1790
+ for (const [field, depth] of [
1791
+ ["near", near],
1792
+ ["far", far]
1793
+ ]) {
1794
+ const focusOverDepth = quantizeF32(focusDistance / depth);
1795
+ const depthFactor = quantizeF32(1 - focusOverDepth);
1796
+ const radius = quantizeF32(cocCoefficient * depthFactor);
1797
+ if (!Number.isFinite(focusOverDepth) || !Number.isFinite(depthFactor) || !Number.isFinite(radius)) {
1798
+ return err(invalid4(`coc.${field}`, radius, "a finite value representable as f32"));
1799
+ }
1800
+ }
1801
+ return ok({
1802
+ focusDistance,
1803
+ fStop,
1804
+ sensorHeight,
1805
+ focalLength: focal,
1806
+ maxRadiusPixels,
1807
+ outputHeight,
1808
+ near,
1809
+ far,
1810
+ cocCoefficient
1811
+ });
1812
+ }
1813
+ function validateDepthOfFieldFrameParams(params, input) {
1814
+ const resolved = resolveDepthOfFieldFrameValues(params, input);
1815
+ return resolved.ok ? ok(void 0) : resolved;
1816
+ }
1817
+ function validateDepthOfFieldParams(input, camera) {
1818
+ if (camera?.projection === "orthographic") {
1819
+ return err(
1820
+ new DepthOfFieldValidationError(
1821
+ "depth-of-field-orthographic-unsupported",
1822
+ { projection: "orthographic" },
1823
+ "DepthOfField requires a perspective Camera",
1824
+ "use a perspective Camera or remove the DepthOfField component"
1825
+ )
1826
+ );
1827
+ }
1828
+ const value = input ?? {};
1829
+ const focusDistance = quantizeF32(
1830
+ value.focusDistance ?? DEFAULT_DEPTH_OF_FIELD_PARAMS.focusDistance
1831
+ );
1832
+ const fStop = quantizeF32(value.fStop ?? DEFAULT_DEPTH_OF_FIELD_PARAMS.fStop);
1833
+ const sensorHeight = quantizeF32(
1834
+ value.sensorHeight ?? DEFAULT_DEPTH_OF_FIELD_PARAMS.sensorHeight
1835
+ );
1836
+ const maxRadiusPixels = quantizeF32(
1837
+ value.maxRadiusPixels ?? DEFAULT_DEPTH_OF_FIELD_PARAMS.maxRadiusPixels
1838
+ );
1839
+ const qualityValue = quantizeF32(value.quality ?? DepthOfFieldQualityValue.medium);
1840
+ const blurSideValue = quantizeF32(value.blurSide ?? DepthOfFieldSideValue.both);
1841
+ const quality2 = depthOfFieldQualityFromF32(qualityValue);
1842
+ const blurSide = depthOfFieldSideFromF32(blurSideValue);
1843
+ if (!Number.isFinite(focusDistance) || focusDistance <= 0) {
1844
+ return err(
1845
+ invalid4("focusDistance", value.focusDistance ?? focusDistance, "a finite positive f32 value")
1846
+ );
1847
+ }
1848
+ if (!Number.isFinite(fStop) || fStop < MIN_F_STOP || fStop > MAX_F_STOP) {
1849
+ return err(
1850
+ invalid4("fStop", value.fStop ?? fStop, "a finite f32 value in [0.7, 32]", [
1851
+ MIN_F_STOP,
1852
+ MAX_F_STOP
1853
+ ])
1854
+ );
1855
+ }
1856
+ if (!Number.isFinite(sensorHeight) || sensorHeight <= 0) {
1857
+ return err(
1858
+ invalid4("sensorHeight", value.sensorHeight ?? sensorHeight, "a finite positive f32 value")
1859
+ );
1860
+ }
1861
+ if (!Number.isFinite(maxRadiusPixels) || maxRadiusPixels < 0 || maxRadiusPixels > 32) {
1862
+ return err(
1863
+ invalid4(
1864
+ "maxRadiusPixels",
1865
+ value.maxRadiusPixels ?? maxRadiusPixels,
1866
+ "a finite f32 value in [0, 32]",
1867
+ [0, 32]
1868
+ )
1869
+ );
1870
+ }
1871
+ if (quality2 === void 0) {
1872
+ return err(invalid4("quality", qualityValue, "DepthOfFieldQualityValue.low, medium, or high"));
1873
+ }
1874
+ if (blurSide === void 0) {
1875
+ return err(invalid4("blurSide", blurSideValue, "DepthOfFieldSideValue.both, near, or far"));
1876
+ }
1877
+ let focal = DEFAULT_DEPTH_OF_FIELD_PARAMS.focalLength;
1878
+ if (camera !== void 0) {
1879
+ const fov = quantizeF32(camera.fov);
1880
+ const near = quantizeF32(camera.near);
1881
+ const far = quantizeF32(camera.far);
1882
+ if (!Number.isFinite(fov) || fov <= 0 || fov >= Math.PI) {
1883
+ return err(invalid4("fov", camera.fov, "a finite f32 value in (0, PI)"));
897
1884
  }
898
- if (reservations.length === 0) {
899
- for (const candidate of selectedCandidates) {
900
- if (useReadyQueue) this.facets.requeueQueryCandidate(candidate.view, candidate.primitive);
901
- }
902
- return void 0;
1885
+ if (!Number.isFinite(near) || !Number.isFinite(far) || near <= 0 || far <= near) {
1886
+ return err(
1887
+ invalid4("near/far", [camera.near, camera.far], "finite f32 near > 0 and far > near")
1888
+ );
903
1889
  }
904
- if (reservations.length < selectedCandidates.length) {
905
- for (const reservation of reservations) this.pool.cancel(reservation);
906
- for (const candidate of selectedCandidates) {
907
- if (useReadyQueue) this.facets.requeueQueryCandidate(candidate.view, candidate.primitive);
908
- }
909
- return void 0;
1890
+ focal = quantizeF32(focalLength(sensorHeight, fov));
1891
+ if (!Number.isFinite(focal) || focal <= 0) {
1892
+ return err(invalid4("focalLength", focal, "a finite positive f32 value"));
910
1893
  }
911
- const resources = this.ensureResources(reservations[0]?.pageIndex ?? 0);
912
- if (resources === void 0) {
913
- for (const reservation of reservations) this.pool.cancel(reservation);
914
- this.lastPrepareUnavailable = true;
915
- for (const candidate of selectedCandidates) {
916
- if (useReadyQueue) this.facets.requeueQueryCandidate(candidate.view, candidate.primitive);
917
- }
918
- return void 0;
1894
+ if (focusDistance < near || focusDistance > far) {
1895
+ return err(
1896
+ invalid4("focusDistance", focusDistance, `a value in [${near}, ${far}]`, [near, far])
1897
+ );
919
1898
  }
920
- const selected = selectedCandidates;
921
- let proxyFloatCount = 0;
922
- for (const candidate of selected) {
923
- const vertices = candidate.proxyVertices;
924
- if (vertices === void 0) continue;
925
- if (proxyFloatCount + vertices.length > this.proxyScratch.length) {
926
- for (const reservation of reservations) this.pool.cancel(reservation);
927
- for (const queuedCandidate of selectedCandidates) {
928
- if (useReadyQueue) {
929
- this.facets.requeueQueryCandidate(queuedCandidate.view, queuedCandidate.primitive);
930
- }
931
- }
932
- this.lastPrepareUnavailable = true;
933
- return void 0;
934
- }
935
- this.proxyScratch.set(vertices, proxyFloatCount);
936
- proxyFloatCount += vertices.length;
1899
+ if (focusDistance <= focal) {
1900
+ return err(
1901
+ invalid4("focusDistance", focusDistance, `greater than focalLength in f32 (${focal})`)
1902
+ );
937
1903
  }
938
- const pending = {
939
- reservations,
940
- resources,
941
- candidates: selected,
942
- sampleCount,
943
- endedCount: 0,
944
- proxyFloatCount
1904
+ }
1905
+ return ok(
1906
+ Object.freeze({
1907
+ focusDistance,
1908
+ fStop,
1909
+ sensorHeight,
1910
+ maxRadiusPixels,
1911
+ quality: quality2,
1912
+ blurSide,
1913
+ focalLength: focal
1914
+ })
1915
+ );
1916
+ }
1917
+ function resolveDepthOfFieldParams(input, camera) {
1918
+ if (input === void 0) return ok(void 0);
1919
+ return validateDepthOfFieldParams(input, camera);
1920
+ }
1921
+ function depthOfFieldTapCount(quality2) {
1922
+ switch (quality2) {
1923
+ case "low":
1924
+ return 16;
1925
+ case "medium":
1926
+ return 32;
1927
+ case "high":
1928
+ return 64;
1929
+ }
1930
+ }
1931
+ function depthOfFieldSideCode(side) {
1932
+ switch (side) {
1933
+ case "both":
1934
+ return DepthOfFieldSideValue.both;
1935
+ case "near":
1936
+ return DepthOfFieldSideValue.near;
1937
+ case "far":
1938
+ return DepthOfFieldSideValue.far;
1939
+ }
1940
+ }
1941
+ function depthOfFieldQualityCode(quality2) {
1942
+ switch (quality2) {
1943
+ case "low":
1944
+ return DepthOfFieldQualityValue.low;
1945
+ case "medium":
1946
+ return DepthOfFieldQualityValue.medium;
1947
+ case "high":
1948
+ return DepthOfFieldQualityValue.high;
1949
+ }
1950
+ }
1951
+ function signedDepthOfFieldCoC(params, viewDepth, outputHeight) {
1952
+ if (!Number.isFinite(viewDepth) || viewDepth <= 0 || !Number.isFinite(outputHeight) || outputHeight <= 0 || !Number.isFinite(params.focalLength) || params.focalLength <= 0 || !Number.isFinite(params.sensorHeight) || params.sensorHeight <= 0 || !Number.isFinite(params.fStop) || params.fStop <= 0 || !Number.isFinite(params.focusDistance) || params.focusDistance <= params.focalLength) {
1953
+ return 0;
1954
+ }
1955
+ const focusDistance = quantizeF32(params.focusDistance);
1956
+ const depth = quantizeF32(viewDepth);
1957
+ const focalLength2 = quantizeF32(params.focalLength);
1958
+ const sensorHeight = quantizeF32(params.sensorHeight);
1959
+ const fStop = quantizeF32(params.fStop);
1960
+ const coefficient = frameCocCoefficient(
1961
+ { focusDistance, focalLength: focalLength2, sensorHeight, fStop },
1962
+ outputHeight
1963
+ );
1964
+ const depthFactor = quantizeF32(1 - quantizeF32(focusDistance / depth));
1965
+ const radius = quantizeF32(coefficient * depthFactor);
1966
+ const limit = Number.isFinite(params.maxRadiusPixels) ? quantizeF32(params.maxRadiusPixels) : Number.POSITIVE_INFINITY;
1967
+ return Math.max(-limit, Math.min(limit, Number.isFinite(radius) ? radius : 0));
1968
+ }
1969
+ function packDepthOfFieldParams(params, input) {
1970
+ const frameValues = resolveDepthOfFieldFrameValues(params, input);
1971
+ if (!frameValues.ok) throw frameValues.error;
1972
+ const {
1973
+ focusDistance,
1974
+ fStop,
1975
+ sensorHeight,
1976
+ focalLength: focal,
1977
+ outputHeight,
1978
+ maxRadiusPixels,
1979
+ near,
1980
+ far,
1981
+ cocCoefficient
1982
+ } = frameValues.value;
1983
+ const payload = new Float32Array(DEPTH_OF_FIELD_PARAMS_BYTE_SIZE / 4);
1984
+ payload.set([
1985
+ focusDistance,
1986
+ fStop,
1987
+ sensorHeight,
1988
+ focal,
1989
+ outputHeight,
1990
+ maxRadiusPixels,
1991
+ depthOfFieldSideCode(params.blurSide),
1992
+ depthOfFieldQualityCode(params.quality),
1993
+ near,
1994
+ far,
1995
+ input.useTemporalDepth ? 1 : 0,
1996
+ 0,
1997
+ // row2.w reserved
1998
+ cocCoefficient,
1999
+ // row3.x / WGSL reserved.x
2000
+ 0,
2001
+ 0,
2002
+ 0
2003
+ ]);
2004
+ return new Uint8Array(payload.buffer);
2005
+ }
2006
+ function hasVolumetricFogCapability(caps, shaders) {
2007
+ return caps.compute && caps.storageTexture && shaders !== void 0;
2008
+ }
2009
+ function resolveVolumetricFogLightPair(world, point, spot) {
2010
+ const expected = "PointLight + SpotLight in the same World";
2011
+ if (!world.get(point, Entity).ok || !world.get(spot, Entity).ok) {
2012
+ return {
2013
+ status: "unresolved",
2014
+ reason: "missing-entity",
2015
+ point,
2016
+ spot,
2017
+ expected,
2018
+ actual: "point or spot entity is absent from this World",
2019
+ hint: "assign two live entities from the same World and retry"
945
2020
  };
946
- this.pendingFrame = pending;
947
- return this.projection;
948
2021
  }
949
- inspect() {
950
- return this.pool.inspect();
2022
+ if (point === spot) {
2023
+ return {
2024
+ status: "unresolved",
2025
+ reason: "same-entity",
2026
+ point,
2027
+ spot,
2028
+ expected,
2029
+ actual: "PointLight and SpotLight share one EntityHandle",
2030
+ hint: "spawn PointLight and SpotLight on two different entities"
2031
+ };
2032
+ }
2033
+ const pointValid = world.hasComponent(point, PointLight) && world.hasComponent(point, Transform);
2034
+ const spotValid = world.hasComponent(spot, SpotLight) && world.hasComponent(spot, Transform);
2035
+ if (!pointValid || !spotValid) {
2036
+ return {
2037
+ status: "unresolved",
2038
+ reason: "wrong-component",
2039
+ point,
2040
+ spot,
2041
+ expected,
2042
+ actual: `pointValid=${pointValid}; spotValid=${spotValid}`,
2043
+ hint: "attach PointLight and SpotLight with Transform to separate same-World entities"
2044
+ };
951
2045
  }
952
- get prepareUnavailable() {
953
- return this.lastPrepareUnavailable;
2046
+ return { status: "available", mode: "point-spot", point, spot };
2047
+ }
2048
+ function resolveSelectedVolumetricLight(world, entity) {
2049
+ const expected = "DirectionalLight | PointLight | SpotLight";
2050
+ if (!world.get(entity, Entity).ok) {
2051
+ return {
2052
+ status: "unresolved",
2053
+ reason: "missing-entity",
2054
+ entity,
2055
+ expected,
2056
+ actual: "missing or foreign World entity",
2057
+ hint: "assign VolumetricFog.light to a live DirectionalLight or SpotLight in this World"
2058
+ };
954
2059
  }
955
- /** Map-only latency from the submitted query page; queue wait is excluded. */
956
- inspectQueryLatency() {
957
- if (this.queryLatencySamplesUs.length === 0) {
958
- return { median: 0, p95: 0, last: this.lastQueryLatencyUs };
959
- }
960
- const sorted = [...this.queryLatencySamplesUs].sort((left, right) => left - right);
961
- const rank = (percentile) => sorted[Math.max(0, Math.ceil(sorted.length * percentile) - 1)] ?? 0;
2060
+ const hasDirectional = world.hasComponent(entity, DirectionalLight);
2061
+ const hasSpot = world.hasComponent(entity, SpotLight);
2062
+ if (hasDirectional && hasSpot) {
962
2063
  return {
963
- median: rank(0.5),
964
- p95: rank(0.95),
965
- last: this.lastQueryLatencyUs
2064
+ status: "unresolved",
2065
+ reason: "ambiguous",
2066
+ entity,
2067
+ expected,
2068
+ actual: "DirectionalLight + SpotLight",
2069
+ hint: "remove one mutually exclusive light component from the selected entity"
966
2070
  };
967
2071
  }
968
- /**
969
- * Wait until query readbacks submitted before the current observation have
970
- * settled. The renderer receipt only fences command submission; query
971
- * completion also includes the asynchronous map/unmap transition. Keeping
972
- * that fence here makes `renderer.observe()` publish the latest visibility
973
- * result and measured map latency instead of racing the readback promise.
974
- */
975
- async waitForCompletions() {
976
- for (; ; ) {
977
- const completions = [...this.activeCompletions.values()];
978
- if (completions.length === 0) return;
979
- await Promise.all(completions);
980
- }
2072
+ if (hasDirectional) return { status: "available", entity, kind: "directional" };
2073
+ if (world.hasComponent(entity, PointLight) && world.hasComponent(entity, Transform)) {
2074
+ return { status: "available", entity, kind: "point" };
981
2075
  }
982
- /** Count a successful frame for hidden rows that were not queried this frame. */
983
- advanceSuccessfulSubmit(submissionGeneration) {
984
- this.facets.advanceSuccessfulSubmits(submissionGeneration);
985
- }
986
- dispose() {
987
- if (this.disposed) return;
988
- this.disposed = true;
989
- this.pool.dispose();
990
- for (const resource of this.resources.values()) {
991
- const completion = this.activeCompletions.get(resource);
992
- if (completion === void 0) {
993
- this.destroyResources(resource);
994
- } else {
995
- void completion.then(() => this.destroyResources(resource));
2076
+ if (hasSpot && world.hasComponent(entity, Transform)) {
2077
+ return { status: "available", entity, kind: "spot" };
2078
+ }
2079
+ const actual = hasSpot ? "SpotLight without Transform" : world.hasComponent(entity, PointLight) ? "PointLight" : "other component";
2080
+ return {
2081
+ status: "unresolved",
2082
+ reason: "wrong-component",
2083
+ entity,
2084
+ expected,
2085
+ actual,
2086
+ hint: "add exactly one supported light component and a Transform for PointLight or SpotLight in the same World"
2087
+ };
2088
+ }
2089
+ function resolveIntegratedVolumeConsumer(resource, consumer) {
2090
+ return { consumer, resource };
2091
+ }
2092
+ var FACE_ORIENTATION = [
2093
+ { direction: [1, 0, 0], up: [0, 1, 0] },
2094
+ { direction: [-1, 0, 0], up: [0, 1, 0] },
2095
+ { direction: [0, 1, 0], up: [0, 0, -1] },
2096
+ { direction: [0, -1, 0], up: [0, 0, 1] },
2097
+ { direction: [0, 0, 1], up: [0, 1, 0] },
2098
+ { direction: [0, 0, -1], up: [0, 1, 0] }
2099
+ ];
2100
+ function assertRange(near, far) {
2101
+ if (!Number.isFinite(near) || near <= 0 || !Number.isFinite(far) || far <= near) {
2102
+ throw new RangeError("CubeCamera requires finite near > 0 and far > near.");
2103
+ }
2104
+ }
2105
+ function buildCubeCameraFaceViews(input) {
2106
+ assertRange(input.near, input.far);
2107
+ const views = [];
2108
+ for (const [index, face] of CUBE_CAMERA_FACE_ORDER.entries()) {
2109
+ const orientation = FACE_ORIENTATION[index];
2110
+ if (orientation === void 0)
2111
+ throw new Error(`CubeCamera face orientation missing at ${index}`);
2112
+ const target = [
2113
+ (input.position[0] ?? 0) + orientation.direction[0],
2114
+ (input.position[1] ?? 0) + orientation.direction[1],
2115
+ (input.position[2] ?? 0) + orientation.direction[2]
2116
+ ];
2117
+ const view = mat4.lookAt(mat4.create(), input.position, target, orientation.up);
2118
+ const projection = mat4.perspective(mat4.create(), Math.PI / 2, 1, input.near, input.far);
2119
+ const viewProjection = mat4.multiply(mat4.create(), projection, view);
2120
+ views.push({
2121
+ position: input.position,
2122
+ face,
2123
+ direction: orientation.direction,
2124
+ up: orientation.up,
2125
+ near: input.near,
2126
+ far: input.far,
2127
+ view,
2128
+ projection,
2129
+ viewProjection
2130
+ });
2131
+ }
2132
+ return views;
2133
+ }
2134
+
2135
+ // src/mesh-material-bindings.ts
2136
+ var EMPTY_RESIDENCY = Object.freeze({
2137
+ readiness: "pending",
2138
+ samplers: Object.freeze([]),
2139
+ textures: Object.freeze([])
2140
+ });
2141
+ function projectMeshMaterialBindingObservation(input) {
2142
+ const residency = input.bindings.map((_, index) => {
2143
+ const value = input.residency?.[index] ?? EMPTY_RESIDENCY;
2144
+ return Object.freeze({
2145
+ ...value,
2146
+ samplers: Object.freeze(value.samplers.map((sampler) => Object.freeze({ ...sampler }))),
2147
+ textures: Object.freeze(value.textures.map((texture) => Object.freeze({ ...texture }))),
2148
+ ...value.preparationFailure === void 0 ? {} : {
2149
+ preparationFailure: Object.freeze({
2150
+ ...value.preparationFailure,
2151
+ ...value.preparationFailure.detail === void 0 ? {} : { detail: Object.freeze({ ...value.preparationFailure.detail }) }
2152
+ })
996
2153
  }
997
- }
998
- this.resources.clear();
999
- if (this.proxyBuffer !== void 0) this.device.destroyBuffer(this.proxyBuffer);
1000
- this.proxyBuffer = void 0;
1001
- this.proxyPipeline = void 0;
1002
- this.proxyPipelineSampleCount = void 0;
1003
- this.candidateSource = void 0;
1004
- this.validCandidates = [];
1005
- this.candidateByKey.clear();
1006
- this.lastPrepareUnavailable = false;
1007
- this.pendingFrame = void 0;
1008
- }
1009
- ensureResources(pageIndex) {
1010
- const existing = this.resources.get(pageIndex);
1011
- if (existing !== void 0) return existing;
1012
- const created = createOcclusionQueryResources(this.device, pageIndex);
1013
- if (!created.ok) return void 0;
1014
- this.resources.set(pageIndex, created.value);
1015
- return created.value;
1016
- }
1017
- commit(pending, submitted, submissionGeneration, profilePhase) {
1018
- if (this.pendingFrame !== pending) return;
1019
- this.pendingFrame = void 0;
1020
- const tickets = [];
1021
- const accepted = profilePhase === void 0 ? this.publishAndApplySubmit(pending, submitted, submissionGeneration, tickets) : profilePhase(
1022
- "record/occlusion-query-submit",
1023
- () => this.publishAndApplySubmit(pending, submitted, submissionGeneration, tickets)
1024
- );
1025
- if (!accepted) {
1026
- for (const reservation of pending.reservations) this.pool.cancel(reservation);
1027
- for (const candidate of pending.candidates) {
1028
- this.facets.applyConfidence(candidate.view, candidate.primitive, {
1029
- type: "failure",
1030
- submissionGeneration
1031
- });
2154
+ });
2155
+ });
2156
+ return {
2157
+ worldId: input.worldId,
2158
+ ...input.worldIdentity === void 0 ? {} : { worldIdentity: input.worldIdentity },
2159
+ entityKey: input.entityKey,
2160
+ bindings: Object.freeze([...input.bindings]),
2161
+ diagnostics: Object.freeze([...input.diagnostics]),
2162
+ residency: Object.freeze(residency)
2163
+ };
2164
+ }
2165
+ function summarizeMeshMaterialBindings(observations) {
2166
+ let ready = 0;
2167
+ let pending = 0;
2168
+ let failed = 0;
2169
+ let lastKnownGood = 0;
2170
+ let textureCount = 0;
2171
+ let samplerCount = 0;
2172
+ for (const observation of observations) {
2173
+ for (const residency of observation.residency) {
2174
+ switch (residency.readiness) {
2175
+ case "ready":
2176
+ ready += 1;
2177
+ break;
2178
+ case "pending":
2179
+ pending += 1;
2180
+ break;
2181
+ case "failed":
2182
+ failed += 1;
2183
+ break;
2184
+ case "last-known-good":
2185
+ lastKnownGood += 1;
2186
+ break;
1032
2187
  }
1033
- return;
1034
- }
1035
- if (profilePhase === void 0) {
1036
- this.facets.advanceSuccessfulSubmits(submissionGeneration);
1037
- } else {
1038
- profilePhase(
1039
- "record/occlusion-global-advance",
1040
- () => this.facets.advanceSuccessfulSubmits(submissionGeneration)
1041
- );
2188
+ textureCount += residency.textures.length;
2189
+ samplerCount += residency.samplers.length;
1042
2190
  }
1043
- const completion = this.completeAfterSubmit(tickets, pending);
1044
- this.activeCompletions.set(pending.resources, completion);
1045
- void completion.then(() => {
1046
- if (this.activeCompletions.get(pending.resources) === completion) {
1047
- this.activeCompletions.delete(pending.resources);
1048
- }
2191
+ }
2192
+ return {
2193
+ total: ready + pending + failed + lastKnownGood,
2194
+ ready,
2195
+ pending,
2196
+ failed,
2197
+ lastKnownGood,
2198
+ textureCount,
2199
+ samplerCount
2200
+ };
2201
+ }
2202
+ function resolveMeshMaterialBindings(mesh, rendererOverrides, deps) {
2203
+ const bindings = [];
2204
+ const diagnostics = [];
2205
+ if (!Array.isArray(mesh.materialSlots)) {
2206
+ return { ok: false, code: "mesh-material-slots-missing" };
2207
+ }
2208
+ if (rendererOverrides.length > mesh.materialSlots.length) {
2209
+ diagnostics.push({
2210
+ code: "mesh-renderer-material-override-overflow",
2211
+ slotIndex: mesh.materialSlots.length
1049
2212
  });
1050
2213
  }
1051
- publishAndApplySubmit(pending, submitted, submissionGeneration, tickets) {
1052
- for (const reservation of pending.reservations) {
1053
- const ticket = this.pool.publish(reservation, { submitted, submissionGeneration });
1054
- if (ticket !== void 0) tickets.push(ticket);
2214
+ for (let slotIndex = 0; slotIndex < mesh.materialSlots.length; slotIndex++) {
2215
+ const override = rendererOverrides[slotIndex] ?? 0;
2216
+ if (override !== 0 && deps.isValidOverride(override)) {
2217
+ bindings.push({ handle: override, source: "renderer-override" });
2218
+ continue;
1055
2219
  }
1056
- if (!submitted || tickets.length !== pending.reservations.length) return false;
1057
- const submitEvent = Object.freeze({ type: "submit", submissionGeneration });
1058
- for (const candidate of pending.candidates) {
1059
- this.facets.applyConfidence(candidate.view, candidate.primitive, submitEvent);
2220
+ if (override !== 0) {
2221
+ diagnostics.push({
2222
+ code: "mesh-renderer-material-override-invalid",
2223
+ slotIndex,
2224
+ handle: override
2225
+ });
1060
2226
  }
1061
- return true;
1062
- }
1063
- destroyResources(resource) {
1064
- if (this.retiredResources.has(resource)) return;
1065
- this.retiredResources.add(resource);
1066
- this.device.destroyBuffer(resource.resolveBuffer);
1067
- this.device.destroyBuffer(resource.stagingBuffer);
1068
- this.device.destroyQuerySet(resource.querySet);
1069
- }
1070
- async completeAfterSubmit(tickets, pending) {
1071
- try {
1072
- await this.device.queue.onSubmittedWorkDone();
1073
- const mapStart = performance.now();
1074
- await runtimeTestHooks?.beforeMap?.();
1075
- const mapped = await pending.resources.stagingBuffer.mapAsync(
1076
- GPU_BUFFER_USAGE_MAP_READ,
1077
- 0,
1078
- pending.reservations.length * 8
1079
- );
1080
- if (!mapped.ok) throw mapped.error;
1081
- this.lastQueryLatencyUs = Math.max(0, Math.round((performance.now() - mapStart) * 1e3));
1082
- this.queryLatencySamplesUs.push(this.lastQueryLatencyUs);
1083
- if (this.queryLatencySamplesUs.length > 64) this.queryLatencySamplesUs.shift();
1084
- const bytes = mapped.value.getMappedRange(0, pending.reservations.length * 8);
1085
- if (!bytes.ok) throw bytes.error;
1086
- const data = new Uint8Array(bytes.value.slice(0));
1087
- const dataView = new DataView(data.buffer);
1088
- const firstQuery = pending.reservations[0]?.queryIndex ?? 0;
1089
- mapped.value.unmap();
1090
- for (let ticketIndex = tickets.length - 1; ticketIndex >= 0; ticketIndex -= 1) {
1091
- const ticket = tickets[ticketIndex];
1092
- if (ticket === void 0) continue;
1093
- const candidateIndex = ticket.queryIndex - firstQuery;
1094
- const samples = dataView.getUint32(candidateIndex * 8, true);
1095
- const completion = this.pool.complete(ticket, samples);
1096
- if (completion.status !== "accepted") continue;
1097
- const candidate = pending.candidates[candidateIndex];
1098
- if (candidate === void 0) continue;
1099
- this.facets.applyConfidence(candidate.view, candidate.primitive, {
1100
- type: "result",
1101
- samples,
1102
- submissionGeneration: ticket.submissionGeneration
1103
- });
1104
- this.facets.applyCompletion(
1105
- candidate.view,
1106
- candidate.primitive,
1107
- candidate.epoch,
1108
- candidate.candidate
1109
- );
1110
- }
1111
- } catch {
1112
- for (const ticket of tickets) {
1113
- this.pool.complete(ticket, Number.NaN);
1114
- const candidate = pending.candidates[ticket.queryIndex - (pending.reservations[0]?.queryIndex ?? 0)];
1115
- if (candidate !== void 0) {
1116
- this.facets.applyConfidence(candidate.view, candidate.primitive, {
1117
- type: "failure",
1118
- submissionGeneration: ticket.submissionGeneration
1119
- });
1120
- }
2227
+ const declaredDefault = mesh.materialSlots[slotIndex]?.defaultMaterial;
2228
+ if (declaredDefault !== void 0) {
2229
+ const handle = deps.resolveMeshDefault(declaredDefault);
2230
+ if (handle === void 0) {
2231
+ return {
2232
+ ok: false,
2233
+ code: "mesh-default-material-not-ready",
2234
+ slotIndex,
2235
+ defaultMaterial: declaredDefault
2236
+ };
1121
2237
  }
2238
+ bindings.push({ handle, source: "mesh-default" });
2239
+ continue;
1122
2240
  }
1123
- }
1124
- encodeProxyBounds(pass, pending) {
1125
- if (pending.proxyFloatCount === 0) {
1126
- return err$1(runtimeError("every occlusion candidate needs conservative proxy geometry"));
1127
- }
1128
- const pipeline = this.ensureProxyPipeline(pending.sampleCount);
1129
- if (pipeline === void 0) {
1130
- for (const reservation of pending.reservations) {
1131
- const began = pass.beginOcclusionQuery(reservation.queryIndex);
1132
- if (!began.ok) return began;
1133
- const ended = pass.endOcclusionQuery();
1134
- if (!ended.ok) return ended;
2241
+ bindings.push({ handle: 0, source: "engine-default" });
2242
+ }
2243
+ return { ok: true, bindings, diagnostics };
2244
+ }
2245
+ function resolveVisibility(world, hierarchy = projectHierarchy(world)) {
2246
+ const query = world.query({ read: [Visibility] });
2247
+ const visitVisibilityRows = (visit) => {
2248
+ if (!query.ok) return;
2249
+ const spans = query.value.spans();
2250
+ if (spans.ok) {
2251
+ for (const span of spans.value) {
2252
+ const states = span.get(Visibility).state;
2253
+ for (let index = 0; index < span.length; index += 1) {
2254
+ visit(span.entities[index], states[index] ?? 0);
2255
+ }
1135
2256
  }
1136
- pending.endedCount = pending.reservations.length;
1137
- return ok$1(void 0);
2257
+ return;
1138
2258
  }
1139
- const buffer = this.ensureProxyBuffer(pending.proxyFloatCount * Float32Array.BYTES_PER_ELEMENT);
1140
- if (buffer === void 0) {
1141
- return err$1(runtimeError("the backend must allocate the bounded proxy geometry buffer"));
2259
+ for (const row of query.value) {
2260
+ visit(row.entity, row.get(Visibility).state);
1142
2261
  }
1143
- const uploaded = this.device.queue.writeBuffer(
1144
- buffer,
1145
- 0,
1146
- this.proxyScratch.subarray(0, pending.proxyFloatCount)
1147
- );
1148
- if (!uploaded.ok) return uploaded;
1149
- pass.setPipeline(pipeline);
1150
- pass.setVertexBuffer(0, buffer);
1151
- let vertexOffset = 0;
1152
- for (let index = 0; index < pending.reservations.length; index += 1) {
1153
- const vertices = pending.candidates[index]?.proxyVertices;
1154
- if (vertices === void 0 || vertices.length === 0) continue;
1155
- const began = pass.beginOcclusionQuery(pending.reservations[index]?.queryIndex ?? 0);
1156
- if (!began.ok) return began;
1157
- pass.draw(vertices.length / 4, 1, vertexOffset / 4, 0);
1158
- const ended = pass.endOcclusionQuery();
1159
- if (!ended.ok) return ended;
1160
- vertexOffset += vertices.length;
2262
+ };
2263
+ let intentCount = 0;
2264
+ let hasAnyHiddenIntent = false;
2265
+ visitVisibilityRows((_entity, rawState) => {
2266
+ intentCount += 1;
2267
+ if (rawState === VisibilityStateValue.hidden) {
2268
+ hasAnyHiddenIntent = true;
1161
2269
  }
1162
- pending.endedCount = pending.reservations.length;
1163
- return ok$1(void 0);
1164
- }
1165
- ensureProxyBuffer(byteLength) {
1166
- if (this.proxyBuffer !== void 0) return this.proxyBuffer;
1167
- const created = this.device.createBuffer({
1168
- label: "occlusion-bounds-proxy-geometry",
1169
- size: Math.max(16, PROXY_GEOMETRY_BYTES, byteLength),
1170
- usage: GPU_BUFFER_USAGE_VERTEX | GPU_BUFFER_USAGE_COPY_DST
2270
+ });
2271
+ let resolveEntity;
2272
+ let hasIntent;
2273
+ const ensureResolver = () => {
2274
+ if (resolveEntity !== void 0) return;
2275
+ const intentByEntity = /* @__PURE__ */ new Map();
2276
+ visitVisibilityRows((entity, rawState) => {
2277
+ const intent = visibilityStateFromU32(rawState);
2278
+ if (intent !== void 0) {
2279
+ intentByEntity.set(entity, intent);
2280
+ }
1171
2281
  });
1172
- if (!created.ok) return void 0;
1173
- this.proxyBuffer = created.value;
1174
- return created.value;
2282
+ hasIntent = (entity) => intentByEntity.has(entity);
2283
+ const resolved = /* @__PURE__ */ new Map();
2284
+ const resolving = /* @__PURE__ */ new Set();
2285
+ resolveEntity = (entity) => {
2286
+ const existing = resolved.get(entity);
2287
+ if (existing !== void 0) return existing;
2288
+ if (resolving.has(entity)) return void 0;
2289
+ const intent = intentByEntity.get(entity) ?? "inherited";
2290
+ resolving.add(entity);
2291
+ let result;
2292
+ if (intent === "hidden") {
2293
+ result = { intent, effective: "hidden", source: "self" };
2294
+ } else if (intent === "visible") {
2295
+ result = { intent, effective: "visible", source: "self" };
2296
+ } else {
2297
+ const parent = hierarchy.getParent(entity);
2298
+ const parentResult = parent === void 0 ? void 0 : resolveEntity?.(parent);
2299
+ result = parentResult === void 0 ? { intent, effective: "visible", source: "default" } : { intent, effective: parentResult.effective, source: "parent" };
2300
+ }
2301
+ resolving.delete(entity);
2302
+ resolved.set(entity, result);
2303
+ return result;
2304
+ };
2305
+ };
2306
+ const snapshot = {
2307
+ diagnostics: hierarchy.diagnostics,
2308
+ hasAnyIntent: intentCount > 0,
2309
+ hasAnyHiddenIntent,
2310
+ get(entity) {
2311
+ ensureResolver();
2312
+ return hasIntent?.(entity) ? resolveEntity?.(entity) : void 0;
2313
+ },
2314
+ effective(entity) {
2315
+ ensureResolver();
2316
+ return resolveEntity?.(entity)?.effective ?? "visible";
2317
+ }
2318
+ };
2319
+ return snapshot;
2320
+ }
2321
+ function selectCloudLayerFrame(candidates, worldTimeSeconds = 0, sunDirection = void 0, sunRadiance = void 0) {
2322
+ if (candidates.length === 0) return ok(void 0);
2323
+ if (candidates.length > 1) return err(new CloudLayerOwnerConflictError(candidates.length));
2324
+ const candidate = candidates[0];
2325
+ if (candidate === void 0) return ok(void 0);
2326
+ const validated = validateCloudLayer(candidate.data);
2327
+ if (!validated.ok) return validated;
2328
+ return ok(
2329
+ Object.freeze({
2330
+ status: "available",
2331
+ entityKey: candidate.entityKey,
2332
+ sourceKey: cloudLayerSourceKey(validated.value),
2333
+ params: validated.value,
2334
+ worldTimeSeconds: Number.isFinite(worldTimeSeconds) ? Math.max(0, worldTimeSeconds) : 0,
2335
+ sunDirection: sunDirection === void 0 ? void 0 : Object.freeze([...sunDirection]),
2336
+ sunRadiance: sunRadiance === void 0 ? void 0 : Object.freeze([...sunRadiance]),
2337
+ revision: 1
2338
+ })
2339
+ );
2340
+ }
2341
+ function extractCloudLayer(world) {
2342
+ const candidates = [];
2343
+ try {
2344
+ const query = world.query({ read: [CloudLayer] });
2345
+ if (!query.ok) return ok(void 0);
2346
+ for (const row of query.value)
2347
+ candidates.push({ entityKey: row.entity, data: row.get(CloudLayer) });
2348
+ } catch {
2349
+ return ok(void 0);
1175
2350
  }
1176
- ensureProxyPipeline(sampleCount) {
1177
- if (this.proxyPipelineSampleCount === sampleCount && this.proxyPipeline !== void 0) {
1178
- return this.proxyPipeline;
2351
+ let sunDirection;
2352
+ let sunRadiance;
2353
+ try {
2354
+ const query = world.query({ read: [DirectionalLight] });
2355
+ if (query.ok) {
2356
+ for (const row of query.value) {
2357
+ const light = row.get(DirectionalLight);
2358
+ const intensity = Number.isFinite(light.intensity) ? Math.max(0, light.intensity) : 0;
2359
+ sunDirection = [
2360
+ -(light.direction[0] ?? 0),
2361
+ -(light.direction[1] ?? -1),
2362
+ -(light.direction[2] ?? 0)
2363
+ ];
2364
+ sunRadiance = [
2365
+ (light.color[0] ?? 1) * intensity,
2366
+ (light.color[1] ?? 1) * intensity,
2367
+ (light.color[2] ?? 1) * intensity
2368
+ ];
2369
+ break;
2370
+ }
1179
2371
  }
1180
- if (this.shaderModuleFactory === void 0) return void 0;
1181
- const module = this.shaderModuleFactory.createShaderModule({
1182
- label: "occlusion-bounds-proxy",
1183
- code: PROXY_BOUNDS_WGSL
1184
- });
1185
- if (!module.ok) return void 0;
1186
- const created = this.device.createRenderPipeline({
1187
- label: "occlusion-bounds-proxy",
1188
- layout: "auto",
1189
- vertex: {
1190
- module: module.value,
1191
- entryPoint: "vs_main",
1192
- buffers: [
1193
- {
1194
- arrayStride: 16,
1195
- stepMode: "vertex",
1196
- attributes: [{ shaderLocation: 0, offset: 0, format: "float32x4" }]
1197
- }
1198
- ]
1199
- },
1200
- // The proxy is recorded in the typed main pass, whose color attachment
1201
- // remains RGBA16Float. Keep the pass compatible while suppressing all
1202
- // color writes; occlusion only consumes depth samples.
1203
- fragment: {
1204
- module: module.value,
1205
- entryPoint: "fs_main",
1206
- targets: [{ format: "rgba16float", writeMask: 0 }]
1207
- },
1208
- primitive: { topology: "triangle-list" },
1209
- depthStencil: {
1210
- format: "depth24plus-stencil8",
1211
- depthWriteEnabled: false,
1212
- depthCompare: "less-equal"
1213
- },
1214
- multisample: { count: sampleCount }
1215
- });
1216
- if (!created.ok) return void 0;
1217
- this.proxyPipeline = created.value;
1218
- this.proxyPipelineSampleCount = sampleCount;
1219
- return created.value;
2372
+ } catch {
1220
2373
  }
1221
- };
1222
- var PROXY_BOUNDS_WGSL = `
1223
- @vertex
1224
- fn vs_main(@location(0) position: vec4f) -> @builtin(position) vec4f {
1225
- return position;
1226
- }
1227
- @fragment
1228
- fn fs_main() -> @location(0) vec4f {
1229
- return vec4f(0.0);
1230
- }
1231
- `;
1232
- function candidateKey(view, primitive) {
1233
- return `${viewKeyId(view)}|${primitiveKeyId(primitive)}`;
1234
- }
1235
- function finiteBounds(bounds) {
1236
- return [...bounds.min, ...bounds.max].every((value) => Number.isFinite(value));
1237
- }
1238
- function runtimeError(hint) {
1239
- return new RhiError({
1240
- code: "webgpu-runtime-error",
1241
- expected: "RenderSystem owns one active occlusion candidate for the shared frame",
1242
- hint
1243
- });
2374
+ return selectCloudLayerFrame(
2375
+ candidates,
2376
+ world.getResource(Time).elapsed,
2377
+ sunDirection,
2378
+ sunRadiance
2379
+ );
2380
+ }
2381
+ function extractVolumetricFog(inputs) {
2382
+ if (inputs.length === 0) return ok({ status: "off" });
2383
+ if (inputs.length > MAX_VOLUMETRIC_FOG_OWNERS)
2384
+ return err(new VolumeOwnerConflictError(inputs.length));
2385
+ const fogs = [];
2386
+ for (const input of inputs) {
2387
+ const validated = validateVolumetricFog(input);
2388
+ if (!validated.ok) return validated;
2389
+ fogs.push(validated.value);
2390
+ }
2391
+ return ok({ status: "available", fogs });
1244
2392
  }
1245
2393
 
1246
2394
  // src/extract/directional-shadow-projection.ts
@@ -1898,10 +3046,10 @@ function extractFrames(worlds, owner, assets, pipelineState, materialSnapshotCac
1898
3046
  });
1899
3047
  const frame = extractFrame(world, prepared);
1900
3048
  framesByWorld.set(wi, frame);
1901
- } catch (err6) {
1902
- if (err6 instanceof MotionBlurValidationError) throw err6;
3049
+ } catch (err13) {
3050
+ if (err13 instanceof MotionBlurValidationError) throw err13;
1903
3051
  try {
1904
- createWorldInternalView(world)._routeError(err6, {
3052
+ createWorldInternalView(world)._routeError(err13, {
1905
3053
  severity: Severity.Error,
1906
3054
  systemName: `RenderSystem.extractFrames(world[${wi}])`
1907
3055
  });
@@ -2061,6 +3209,7 @@ function extractFrames(worlds, owner, assets, pipelineState, materialSnapshotCac
2061
3209
  directionalCsmDirection
2062
3210
  };
2063
3211
  const volumetricFog = resourceOwnerFrame?.volumetricFog;
3212
+ const cloudLayer = resourceOwnerFrame?.cloudLayer;
2064
3213
  const skylight = resourceOwnerFrame?.skylight;
2065
3214
  const skylightCount = resourceOwnerFrame?.skylightCount ?? 0;
2066
3215
  const skybox = resourceOwnerFrame?.skybox;
@@ -2116,6 +3265,7 @@ function extractFrames(worlds, owner, assets, pipelineState, materialSnapshotCac
2116
3265
  reflectionProbes,
2117
3266
  lights,
2118
3267
  ...volumetricFog === void 0 ? {} : { volumetricFog: { ...volumetricFog, worldId: resourceOwner } },
3268
+ ...cloudLayer === void 0 ? {} : { cloudLayer },
2119
3269
  renderables,
2120
3270
  dispatch: dispatchEntries,
2121
3271
  shadowCasterEntityKeys,
@@ -2205,6 +3355,16 @@ function extractFrame(world, context) {
2205
3355
  const directionalLightQuery = world.query({ read: [DirectionalLight] }).unwrap();
2206
3356
  const worldInternal = createWorldInternalView(world);
2207
3357
  let volumetricFog = extractVolumeSnapshot(world, assets, worldInternal);
3358
+ let cloudLayer;
3359
+ const extractedCloudLayer = extractCloudLayer(world);
3360
+ if (extractedCloudLayer.ok) {
3361
+ cloudLayer = extractedCloudLayer.value;
3362
+ } else {
3363
+ worldInternal._routeError(extractedCloudLayer.error, {
3364
+ severity: Severity.Error,
3365
+ systemName: "RenderSystem.extract (cloud-layer)"
3366
+ });
3367
+ }
2208
3368
  const cameraRoles = selectCameraRoles(world, context.cameraEntityKey);
2209
3369
  const cameras = [...cameraRoles.display];
2210
3370
  const auxiliaryCameras = cameraRoles.auxiliary;
@@ -2862,7 +4022,6 @@ function extractFrame(world, context) {
2862
4022
  let dispatch = [];
2863
4023
  const shadowMembershipEntries = [];
2864
4024
  const materialSnapshotCache = /* @__PURE__ */ new Map();
2865
- const instanceSnapshotCache = /* @__PURE__ */ new Map();
2866
4025
  const skinnedMaterialSnapshotCache = /* @__PURE__ */ new Map();
2867
4026
  const meshRendererQuery = context.renderables === "none" ? [] : world.query({
2868
4027
  read: [MeshRenderer],
@@ -2899,14 +4058,18 @@ function extractFrame(world, context) {
2899
4058
  const flushPendingDispatch = (pending) => {
2900
4059
  for (const entry of pending) dispatch.push(entry);
2901
4060
  };
2902
- for (const row of meshRendererQuery) {
2903
- if (context.renderables === "full" && renderableEntities !== void 0 && !renderableEntities.has(row.entity)) {
2904
- continue;
4061
+ const requestedRows = renderableEntities === void 0 ? meshRendererQuery : {
4062
+ *[Symbol.iterator]() {
4063
+ for (const entity of renderableEntities) {
4064
+ const row = meshRendererQuery.at(entity);
4065
+ if (row !== void 0) yield row;
4066
+ }
2905
4067
  }
4068
+ };
4069
+ for (const row of requestedRows) {
2906
4070
  const hasTransform = row.has(Transform);
2907
4071
  const meshFilter = row.get(MeshFilter);
2908
- const instancesData = row.get(Instances);
2909
- const hasInstances = instancesData !== void 0;
4072
+ const hasInstances = row.has(Instances);
2910
4073
  const skin = row.get(Skin);
2911
4074
  const morphWeightsView = worldInternal._getArrayView(row.entity, MorphWeights, "weights");
2912
4075
  const hasMeshFilter = meshFilter !== void 0;
@@ -3120,28 +4283,28 @@ function extractFrame(world, context) {
3120
4283
  } else {
3121
4284
  const resolvedResult = walkMaterialPassesOverSharedRefs(world, tagged, assets);
3122
4285
  if (!resolvedResult.ok) {
3123
- const err6 = resolvedResult.error;
3124
- switch (err6.code) {
4286
+ const err13 = resolvedResult.error;
4287
+ switch (err13.code) {
3125
4288
  case "material-parent-not-found":
3126
4289
  case "material-no-effective-pass":
3127
4290
  case "material-value-unknown":
3128
4291
  case "material-value-type-mismatch":
3129
4292
  case "material-contract-program-mismatch":
3130
- worldInternal._routeError(err6, {
4293
+ worldInternal._routeError(err13, {
3131
4294
  severity: Severity.Error,
3132
- systemName: `RenderSystem.extract (${err6.code})`
4295
+ systemName: `RenderSystem.extract (${err13.code})`
3133
4296
  });
3134
4297
  break;
3135
4298
  case "material-circular-inheritance":
3136
- worldInternal._routeError(err6, {
4299
+ worldInternal._routeError(err13, {
3137
4300
  severity: Severity.Error,
3138
4301
  systemName: "RenderSystem.extract (material-circular-inheritance)"
3139
4302
  });
3140
4303
  break;
3141
4304
  default:
3142
- worldInternal._routeError(err6, {
4305
+ worldInternal._routeError(err13, {
3143
4306
  severity: Severity.Error,
3144
- systemName: `RenderSystem.extract (_materialWalk: ${err6.code})`
4307
+ systemName: `RenderSystem.extract (_materialWalk: ${err13.code})`
3145
4308
  });
3146
4309
  }
3147
4310
  continue;
@@ -3398,6 +4561,12 @@ function extractFrame(world, context) {
3398
4561
  baseColor,
3399
4562
  metallic: metallicPv,
3400
4563
  roughness: roughnessPv,
4564
+ ...resolved.surface?.model === void 0 ? {} : { surfaceModel: resolved.surface.model },
4565
+ deferredPass: allPasses.some(
4566
+ (pass) => String(
4567
+ pass.renderState?.tags?.LightMode ?? pass.name
4568
+ ) === "Deferred"
4569
+ ),
3401
4570
  ...specularColorPv !== void 0 && {
3402
4571
  specularColor: [
3403
4572
  specularColorPv[0] ?? 1,
@@ -3507,6 +4676,7 @@ function extractFrame(world, context) {
3507
4676
  }
3508
4677
  if (isRenderable) {
3509
4678
  let skinSlice;
4679
+ let skinPose;
3510
4680
  const skinIdentity = hasSkin ? skinPaletteIdentity(world, entity) : void 0;
3511
4681
  const releaseSkinIdentity = () => {
3512
4682
  if (skinIdentity !== void 0) {
@@ -3592,10 +4762,19 @@ function extractFrame(world, context) {
3592
4762
  });
3593
4763
  skinPaletteAllocator.writePersistentJointPalette(receipt, ibms, jointWorlds);
3594
4764
  skinSlice = receipt;
4765
+ } else {
4766
+ skinPose = {
4767
+ identity: skinPaletteIdentity(world, entity),
4768
+ generation: (skeletonHandleRaw >>> 0 ^ skeleton.jointCount) >>> 0,
4769
+ jointCount: skeleton.jointCount,
4770
+ ...hasFiniteOrderedLocalAabb(skeleton.bounds) ? { bounds: new Float32Array(skeleton.bounds) } : {},
4771
+ inverseBindMatrices: ibms,
4772
+ jointWorlds
4773
+ };
3595
4774
  }
3596
4775
  }
3597
4776
  }
3598
- if (skinSlice === void 0 && hasSkin) {
4777
+ if (skinSlice === void 0 && skinPose === void 0 && hasSkin) {
3599
4778
  releaseSkinIdentity();
3600
4779
  if (materialSnap.materialShaderId === "forgeax::pbr-skin" || materialProgramSelections?.some(materialProgramSelectionRequiresSkin) === true) {
3601
4780
  pendingDispatch.length = 0;
@@ -3748,45 +4927,26 @@ function extractFrame(world, context) {
3748
4927
  };
3749
4928
  let instancesSnap;
3750
4929
  if (hasInstances) {
3751
- const collectionId = instancesData?.collectionId;
3752
- let collectionResult = collectionId !== void 0 ? instanceSnapshotCache.get(collectionId) : void 0;
3753
- if (collectionResult === void 0) {
3754
- collectionResult = collectionId !== void 0 && collectionId > 0 && instanceCollections !== void 0 ? instanceCollections.snapshot(collectionId) : err(
3755
- new InstanceCollectionError(
3756
- "instance-collection-invalid",
3757
- "Instances.collectionId references a live renderer-owned collection",
3758
- "create the collection through renderer.instances before attaching Instances",
3759
- {
3760
- operation: "inspect",
3761
- expected: "a positive collectionId with a live renderer owner",
3762
- actual: Number(collectionId ?? 0),
3763
- ...collectionId === void 0 ? {} : { collectionId }
3764
- }
3765
- )
3766
- );
3767
- if (collectionId !== void 0)
3768
- instanceSnapshotCache.set(collectionId, collectionResult);
3769
- }
3770
- if (collectionResult === void 0) continue;
3771
- if (!collectionResult.ok) {
3772
- worldInternal._routeError(collectionResult.error, {
4930
+ const transforms = worldInternal._getArrayView(entity, Instances, "transforms") ?? new Float32Array();
4931
+ const invalid5 = validateInstanceTransforms(transforms);
4932
+ if (invalid5 !== void 0) {
4933
+ worldInternal._routeError(invalid5, {
3773
4934
  severity: Severity.Error,
3774
- systemName: "RenderSystem.extract (Instances collection)"
4935
+ systemName: "RenderSystem.extract (Instances transforms)"
3775
4936
  });
3776
4937
  continue;
3777
4938
  }
4939
+ const projected = instanceCollections?.project(world, entity, transforms);
3778
4940
  instancesSnap = {
3779
- transforms: collectionResult.value.transforms,
3780
- generations: collectionResult.value.generations,
3781
- instanceCount: collectionResult.value.count,
3782
- collectionId: collectionResult.value.collectionId,
3783
- revision: collectionResult.value.revision,
3784
- capacity: collectionResult.value.capacity,
3785
- dirtyRanges: collectionResult.value.dirtyRanges,
4941
+ transforms: projected?.transforms ?? new Float32Array(transforms),
4942
+ instanceCount: transforms.length / 16,
3786
4943
  cacheKey: entity,
3787
- // Explicit collection revisions supersede ECS archetype content
3788
- // hashes; retain zero as the legacy sprite-only field value.
3789
- archVersion: 0
4944
+ archVersion: projected === void 0 ? archVersion : 0,
4945
+ ...projected === void 0 ? {} : {
4946
+ collectionId: projected.collectionId,
4947
+ revision: projected.revision,
4948
+ generations: projected.generations
4949
+ }
3790
4950
  };
3791
4951
  }
3792
4952
  const baseRenderable = {
@@ -3803,13 +4963,14 @@ function extractFrame(world, context) {
3803
4963
  entityKey: entity,
3804
4964
  ...authorVisible ? {} : { authorVisible: false },
3805
4965
  ...skinSlice !== void 0 ? { skin: skinSlice } : {},
4966
+ ...skinPose === void 0 ? {} : { skinPose },
3806
4967
  ...skin?.joints === void 0 ? {} : { skinJointEntities: Array.from(skin.joints, (joint) => Number(joint)) },
3807
4968
  ...morph !== void 0 ? { morph } : {},
3808
4969
  ...instancesSnap !== void 0 ? { instances: instancesSnap } : {},
3809
4970
  ...spriteInstancesSnap !== void 0 ? { spriteInstances: spriteInstancesSnap } : {},
3810
4971
  ...pointsLines !== void 0 ? { pointsLines } : {}
3811
4972
  };
3812
- const gpuDrivenDraws = buildGpuDrivenDraws({
4973
+ const gpuDrivenDraws = skinPose === void 0 ? buildGpuDrivenDraws({
3813
4974
  mesh: gpuDrivenMesh,
3814
4975
  materials: materialsArr,
3815
4976
  fallbackMaterial: materialSnap,
@@ -3821,7 +4982,7 @@ function extractFrame(world, context) {
3821
4982
  return lower?.kind === "mesh" ? lower : void 0;
3822
4983
  })
3823
4984
  }
3824
- });
4985
+ }) : [];
3825
4986
  for (const draw of gpuDrivenDraws) {
3826
4987
  if (draw.preparationError === void 0) continue;
3827
4988
  worldInternal._routeError(draw.preparationError, {
@@ -3974,6 +5135,7 @@ function extractFrame(world, context) {
3974
5135
  environment,
3975
5136
  environmentReady: true,
3976
5137
  ...volumetricFog === void 0 ? {} : { volumetricFog },
5138
+ ...cloudLayer === void 0 ? {} : { cloudLayer },
3977
5139
  renderables,
3978
5140
  dispatch,
3979
5141
  shadowCasterEntityKeys,
@@ -4357,8 +5519,8 @@ function internSharedRefFromGuid(world, assetsRef, guid, brand) {
4357
5519
  return handle;
4358
5520
  }
4359
5521
  function resolveVideoFieldHandle(value, world, assetsRef) {
4360
- const texture2 = materialTextureValue(value);
4361
- const textureGuid = assetReferenceText(texture2?.texture ?? value);
5522
+ const texture = materialTextureValue(value);
5523
+ const textureGuid = assetReferenceText(texture?.texture ?? value);
4362
5524
  if (textureGuid === void 0) return void 0;
4363
5525
  const payload = assetsRef.lookup(textureGuid);
4364
5526
  if (payload === void 0 || payload.kind !== "video") return void 0;
@@ -4397,10 +5559,10 @@ function collectMaterialTextureCoordinates(values) {
4397
5559
  }
4398
5560
  return out;
4399
5561
  }
4400
- function materialTextureSourceFields(schema) {
4401
- if (schema === void 0) return void 0;
5562
+ function materialTextureSourceFields(schema2) {
5563
+ if (schema2 === void 0) return void 0;
4402
5564
  const fields = /* @__PURE__ */ new Set();
4403
- for (const entry of schema) {
5565
+ for (const entry of schema2) {
4404
5566
  if (entry.type.startsWith("texture")) fields.add(entry.name);
4405
5567
  }
4406
5568
  return fields;
@@ -4674,6 +5836,12 @@ function resolveMaterialSnapshot(handleRaw, world, assetsRef, materialSnapshotCa
4674
5836
  baseColor,
4675
5837
  metallic: metallicPv,
4676
5838
  roughness: roughnessPv,
5839
+ ...resolved.surface?.model === void 0 ? {} : { surfaceModel: resolved.surface.model },
5840
+ deferredPass: allPasses.some(
5841
+ (pass) => String(
5842
+ pass.renderState?.tags?.LightMode ?? pass.name
5843
+ ) === "Deferred"
5844
+ ),
4677
5845
  ...specularColorPv !== void 0 && {
4678
5846
  specularColor: [
4679
5847
  specularColorPv[0] ?? 1,
@@ -4923,12 +6091,19 @@ function collectCameraSnapshots(world) {
4923
6091
  const cameras = [];
4924
6092
  const cameraQuery = world.query({
4925
6093
  read: [Camera],
4926
- optional: [MotionBlur, DynamicResolution, ScreenSpaceReflection, DepthOfField],
6094
+ optional: [
6095
+ MotionBlur,
6096
+ DynamicResolution,
6097
+ ScreenSpaceReflection,
6098
+ DepthOfField,
6099
+ BarrelDistortion
6100
+ ],
4927
6101
  with: [Transform, GlobalTransform]
4928
6102
  }).unwrap();
4929
6103
  for (const row of cameraQuery) {
4930
6104
  const cam = row.get(Camera);
4931
6105
  const motionBlur = row.get(MotionBlur);
6106
+ const barrelDistortion = row.get(BarrelDistortion);
4932
6107
  const motionBlurResult = resolveMotionBlurParams(motionBlur);
4933
6108
  if (!motionBlurResult.ok) throw motionBlurResult.error;
4934
6109
  const motionBlurParams = motionBlurResult.value;
@@ -4959,6 +6134,8 @@ function collectCameraSnapshots(world) {
4959
6134
  const antialias = antialiasFromF32(cam.antialias);
4960
6135
  const dynamicResolutionResult = validateDynamicResolutionCamera(dynamicResolution, antialias);
4961
6136
  if (!dynamicResolutionResult.ok) throw dynamicResolutionResult.error;
6137
+ const barrelDistortionResult = validateBarrelDistortionParameters(barrelDistortion);
6138
+ if (!barrelDistortionResult.ok) throw barrelDistortionResult.error;
4962
6139
  cameras.push({
4963
6140
  entityKey: entity,
4964
6141
  ...target === void 0 ? {} : { target },
@@ -5007,6 +6184,7 @@ function collectCameraSnapshots(world) {
5007
6184
  targetFps: motionBlurParams.targetFps
5008
6185
  }
5009
6186
  },
6187
+ ...barrelDistortion === void 0 ? {} : { barrelDistortion: barrelDistortionResult.value },
5010
6188
  ...screenSpaceReflection === void 0 ? {} : {
5011
6189
  screenSpaceReflection: {
5012
6190
  maxDistance: screenSpaceReflection.maxDistance,
@@ -5176,9 +6354,9 @@ function selectFogFrame(world) {
5176
6354
  if (state.lkg !== void 0) return state.lkg;
5177
6355
  throw fogSelection.error;
5178
6356
  }
5179
- function textureDigest(texture2) {
6357
+ function textureDigest(texture) {
5180
6358
  let hash = 2166136261;
5181
- for (const byte of texture2.data) hash = Math.imul(hash ^ byte, 16777619) >>> 0;
6359
+ for (const byte of texture.data) hash = Math.imul(hash ^ byte, 16777619) >>> 0;
5182
6360
  return `fnv1a:${hash.toString(16).padStart(8, "0")}`;
5183
6361
  }
5184
6362
  function extractVolumeSnapshot(world, assets, worldInternal) {
@@ -5186,9 +6364,10 @@ function extractVolumeSnapshot(world, assets, worldInternal) {
5186
6364
  const worldTimeSeconds = Number.isFinite(elapsed) ? Math.max(0, elapsed) : 0;
5187
6365
  const query = world.query({ read: [VolumetricFog] }).unwrap();
5188
6366
  const authorings = [];
6367
+ const members = [];
5189
6368
  let selectedLight;
5190
- let first;
5191
6369
  for (const row of query) {
6370
+ selectedLight = void 0;
5192
6371
  const source = row.get(VolumetricFog);
5193
6372
  if (source.light === null) continue;
5194
6373
  const selected = resolveSelectedVolumetricLight(world, source.light);
@@ -5256,10 +6435,25 @@ function extractVolumeSnapshot(world, assets, worldInternal) {
5256
6435
  albedo: [source.albedo[0] ?? 0, source.albedo[1] ?? 0, source.albedo[2] ?? 0],
5257
6436
  emission: [source.emission[0] ?? 0, source.emission[1] ?? 0, source.emission[2] ?? 0],
5258
6437
  anisotropy: source.anisotropy,
5259
- maxDistance: source.maxDistance
6438
+ maxDistance: source.maxDistance,
6439
+ sampling: source.sampling === 1 ? "density" : "noise"
5260
6440
  };
5261
6441
  authorings.push(authoring);
5262
- first ??= { densityHandle, densityAsset, guid };
6442
+ members.push({
6443
+ status: "available",
6444
+ worldTimeSeconds,
6445
+ densityHandle,
6446
+ densityAsset,
6447
+ guid,
6448
+ generation: 1,
6449
+ digest: textureDigest(densityAsset),
6450
+ ...selectedLight === void 0 ? {} : {
6451
+ lightEntity: selectedLight.entity,
6452
+ lightKind: selectedLight.kind,
6453
+ ...selectedLight.pointLightEntity === void 0 ? {} : { pointLightEntity: selectedLight.pointLightEntity },
6454
+ ...selectedLight.spotLightEntity === void 0 ? {} : { spotLightEntity: selectedLight.spotLightEntity }
6455
+ }
6456
+ });
5263
6457
  }
5264
6458
  if (authorings.length === 0) return void 0;
5265
6459
  const extracted = extractVolumetricFog(authorings);
@@ -5268,582 +6462,21 @@ function extractVolumeSnapshot(world, assets, worldInternal) {
5268
6462
  severity: Severity.Error,
5269
6463
  systemName: "RenderSystem.extract (volumetric-fog)"
5270
6464
  });
5271
- return {
5272
- status: "degraded",
5273
- worldTimeSeconds,
5274
- ...first === void 0 ? {} : {
5275
- densityHandle: first.densityHandle,
5276
- densityAsset: first.densityAsset,
5277
- guid: first.guid,
5278
- generation: 1,
5279
- digest: textureDigest(first.densityAsset),
5280
- ...selectedLight === void 0 ? {} : {
5281
- lightEntity: selectedLight.entity,
5282
- lightKind: selectedLight.kind,
5283
- ...selectedLight.pointLightEntity === void 0 ? {} : { pointLightEntity: selectedLight.pointLightEntity },
5284
- ...selectedLight.spotLightEntity === void 0 ? {} : { spotLightEntity: selectedLight.spotLightEntity }
5285
- }
5286
- }
5287
- };
6465
+ return { ...members[0], status: "degraded" };
5288
6466
  }
5289
- if (extracted.value.status === "off" || first === void 0) return { status: "off" };
5290
- const selectedLightFields = selectedLight === void 0 ? {} : {
5291
- lightEntity: selectedLight.entity,
5292
- lightKind: selectedLight.kind,
5293
- ...selectedLight.pointLightEntity === void 0 ? {} : { pointLightEntity: selectedLight.pointLightEntity },
5294
- ...selectedLight.spotLightEntity === void 0 ? {} : { spotLightEntity: selectedLight.spotLightEntity }
5295
- };
5296
- return {
6467
+ if (extracted.value.status === "off") return { status: "off" };
6468
+ const validatedMembers = extracted.value.fogs.map((fog, index) => ({
6469
+ ...members[index],
5297
6470
  status: "available",
5298
- worldTimeSeconds,
5299
- fog: extracted.value.fog,
5300
- densityHandle: first.densityHandle,
5301
- densityAsset: first.densityAsset,
5302
- guid: first.guid,
5303
- generation: 1,
5304
- digest: textureDigest(first.densityAsset),
5305
- ...selectedLightFields
5306
- };
5307
- }
5308
- var PROBE_R_MIN = R_MIN;
5309
- var PROBE_MAX_CONTRIBUTORS = PROBE_BLEND_RECORD_CAPACITY;
5310
- var SH_C0 = 0.28209479177387814;
5311
- var SH_C1 = 0.4886025119029199;
5312
- var SH_C2 = 1.0925484305920792;
5313
- var SH_C20 = 0.31539156525252005;
5314
- var SH_C22 = 0.5462742152960396;
5315
- function finiteVector(values, length) {
5316
- if (values.length !== length) return false;
5317
- for (let index = 0; index < values.length; index += 1) {
5318
- if (!Number.isFinite(values[index])) return false;
5319
- }
5320
- return true;
5321
- }
5322
- function contributorIsActive(probe, radiusMinimum) {
5323
- return probe.admitted && Number.isFinite(probe.distance) && probe.distance >= 0 && Number.isFinite(probe.radius) && probe.radius >= radiusMinimum && probe.distance < probe.radius && finiteVector(probe.irradiance, 27);
5324
- }
5325
- function emptyReceipt(radiusMinimum) {
5326
- return {
5327
- activeIdentities: [],
5328
- admittedIdentities: [],
5329
- rejectedIdentities: [],
5330
- stableOrder: [],
5331
- capacity: PROBE_MAX_CONTRIBUTORS,
5332
- scaleRadius: radiusMinimum,
5333
- finite: true
5334
- };
5335
- }
5336
- function admitProbeContributors(candidates, radiusMinimum = PROBE_R_MIN) {
5337
- const active = candidates.filter((candidate) => contributorIsActive(candidate, radiusMinimum));
5338
- const overflowed = active.length > PROBE_MAX_CONTRIBUTORS;
5339
- const stableActive = active.slice().sort((left, right) => left.identity.localeCompare(right.identity));
5340
- const admitted = stableActive.slice(0, PROBE_MAX_CONTRIBUTORS);
5341
- const rejected = stableActive.slice(PROBE_MAX_CONTRIBUTORS);
5342
- const scaleRadius = active.reduce(
5343
- (minimum, candidate) => Math.min(minimum, candidate.radius),
5344
- active[0]?.radius ?? radiusMinimum
5345
- );
5346
- const receipt = {
5347
- activeIdentities: stableActive.map((candidate) => candidate.identity),
5348
- admittedIdentities: admitted.map((candidate) => candidate.identity),
5349
- rejectedIdentities: rejected.map((candidate) => candidate.identity),
5350
- stableOrder: admitted.map((candidate) => candidate.identity),
5351
- capacity: PROBE_MAX_CONTRIBUTORS,
5352
- ...overflowed ? { overflowReason: "active-count-exceeds-capacity" } : {},
5353
- scaleRadius,
5354
- finite: stableActive.every((candidate) => Number.isFinite(candidate.radius))
5355
- };
5356
- return {
5357
- active: stableActive,
5358
- admitted,
5359
- rejected,
5360
- receipt,
5361
- ...overflowed ? {
5362
- error: {
5363
- code: "capacity-exceeded",
5364
- activeCount: active.length,
5365
- capacity: PROBE_MAX_CONTRIBUTORS,
5366
- receipt
5367
- }
5368
- } : {}
5369
- };
5370
- }
5371
- function invalidInputResult(radiusMinimum, code) {
5372
- const receipt = emptyReceipt(radiusMinimum);
5373
- return {
5374
- terms: [],
5375
- rStar: radiusMinimum,
5376
- Q: 0,
5377
- C: 0,
5378
- S: 1,
5379
- SHPreblend: new Array(27).fill(0),
5380
- skyIrradiance: [0, 0, 0],
5381
- diffuse: [0, 0, 0],
5382
- finite: false,
5383
- receipt,
5384
- scaledQLogOffset: 0,
5385
- error: { code, activeCount: 0, capacity: PROBE_MAX_CONTRIBUTORS, receipt }
5386
- };
5387
- }
5388
- function neumaierSum(values) {
5389
- let sum = 0;
5390
- let correction = 0;
5391
- for (const value of values) {
5392
- const next = sum + value;
5393
- if (Math.abs(sum) >= Math.abs(value)) correction += sum - next + value;
5394
- else correction += value - next + sum;
5395
- sum = next;
5396
- }
5397
- return sum + correction;
5398
- }
5399
- function roundProbeToF32(value) {
5400
- return Number.isFinite(value) ? new Float32Array([value])[0] : void 0;
5401
- }
5402
- function scaledProbeWeight(distance, radius, radiusMinimum = PROBE_R_MIN) {
5403
- if (!Number.isFinite(distance) || distance < 0 || !Number.isFinite(radius) || radius < radiusMinimum || distance >= radius) {
5404
- return { coverage: 0, q: 0 };
5405
- }
5406
- const ratio = distance / radius;
5407
- const coverage = 1 - ratio * ratio;
5408
- return { coverage, q: coverage / (radius * radius) };
5409
- }
5410
- function sh9Irradiance(coefficients, normal) {
5411
- const x = normal[0];
5412
- const y = normal[1];
5413
- const z = normal[2];
5414
- const basis = [
5415
- SH_C0,
5416
- SH_C1 * y,
5417
- SH_C1 * z,
5418
- SH_C1 * x,
5419
- SH_C2 * x * y,
5420
- SH_C2 * y * z,
5421
- SH_C20 * (3 * z * z - 1),
5422
- SH_C2 * x * z,
5423
- SH_C22 * (x * x - y * y)
5424
- ];
5425
- const result = [0, 0, 0];
5426
- for (let band = 0; band < basis.length; band += 1) {
5427
- const factor = basis[band] ?? 0;
5428
- const offset = band * 3;
5429
- result[0] = (result[0] ?? 0) + (coefficients[offset] ?? 0) * factor;
5430
- result[1] = (result[1] ?? 0) + (coefficients[offset + 1] ?? 0) * factor;
5431
- result[2] = (result[2] ?? 0) + (coefficients[offset + 2] ?? 0) * factor;
5432
- }
5433
- return result;
5434
- }
5435
- function vectorNeumaierSum(values, width) {
5436
- return Array.from(
5437
- { length: width },
5438
- (_, channel) => neumaierSum(values.map((value) => value[channel] ?? 0))
5439
- );
5440
- }
5441
- function blendProbeCoefficients(contributors, radiusMinimum) {
5442
- if (contributors.length > PROBE_MAX_CONTRIBUTORS || contributors.some((contributor) => !contributorIsActive(contributor, radiusMinimum))) {
5443
- return void 0;
5444
- }
5445
- const rStar = contributors.reduce(
5446
- (minimum, contributor) => Math.min(minimum, contributor.radius),
5447
- contributors[0]?.radius ?? radiusMinimum
5448
- );
5449
- const rawTerms = contributors.map((contributor) => {
5450
- const { coverage } = scaledProbeWeight(contributor.distance, contributor.radius, radiusMinimum);
5451
- const scaledRatio = rStar / contributor.radius;
5452
- return {
5453
- contributor,
5454
- coverage,
5455
- // Diagnostic q is never used for Q, alpha, C, or SH accumulation.
5456
- q: coverage / (contributor.radius * contributor.radius),
5457
- scaledQ: coverage * scaledRatio * scaledRatio
5458
- };
5459
- });
5460
- const scaledQLogOffset = 0;
5461
- const finiteTerms = rawTerms;
5462
- if (finiteTerms.some((term) => !Number.isFinite(term.scaledQ))) return void 0;
5463
- const Q = neumaierSum(finiteTerms.map((term) => term.scaledQ));
5464
- const terms = finiteTerms.map((term) => ({
5465
- identity: term.contributor.identity,
5466
- distance: term.contributor.distance,
5467
- radius: term.contributor.radius,
5468
- coverage: term.coverage,
5469
- q: term.q,
5470
- scaledQ: term.scaledQ,
5471
- qHat: term.scaledQ,
5472
- alpha: Q > 0 ? term.scaledQ / Q : 0
6471
+ fog
5473
6472
  }));
5474
- let product = 1;
5475
- for (const term of finiteTerms) product *= 1 - term.coverage;
5476
- const C = 1 - product;
5477
- const S = 1 - C;
5478
- const weightedSh = finiteTerms.map(
5479
- (term) => Array.from(term.contributor.irradiance, (value) => value * (Q > 0 ? term.scaledQ / Q : 0))
5480
- );
5481
- const SHPreblend = vectorNeumaierSum(weightedSh, 27).map((value) => value * C);
5482
- const finite = Number.isFinite(Q) && Number.isFinite(C) && Number.isFinite(S) && SHPreblend.every(Number.isFinite) && finiteTerms.every((term) => Number.isFinite(term.scaledQ));
5483
- const receipt = {
5484
- activeIdentities: contributors.map((contributor) => contributor.identity),
5485
- admittedIdentities: contributors.map((contributor) => contributor.identity),
5486
- rejectedIdentities: [],
5487
- stableOrder: contributors.map((contributor) => contributor.identity),
5488
- capacity: PROBE_MAX_CONTRIBUTORS,
5489
- scaleRadius: rStar,
5490
- finite
5491
- };
5492
- return {
5493
- terms,
5494
- rStar,
5495
- Q,
5496
- C,
5497
- S,
5498
- SHPreblend,
5499
- finite,
5500
- receipt,
5501
- scaledQLogOffset
5502
- };
5503
- }
5504
- function blendLightProbes(input) {
5505
- const radiusMinimum = input.radiusMinimum ?? PROBE_R_MIN;
5506
- const normal = input.normal;
5507
- const sky = input.skyIrradiance;
5508
- if (!finiteVector(normal, 3) || !finiteVector(sky, 3)) {
5509
- return invalidInputResult(radiusMinimum, "invalid-admitted-prefix");
5510
- }
5511
- const coefficients = blendProbeCoefficients(input.contributors, radiusMinimum);
5512
- if (coefficients === void 0)
5513
- return invalidInputResult(radiusMinimum, "invalid-admitted-prefix");
5514
- const shIrradiance = sh9Irradiance(coefficients.SHPreblend, normal);
5515
- const diffuse = [0, 1, 2].map(
5516
- (channel) => Math.max(shIrradiance[channel] ?? 0, 0) + coefficients.S * (sky[channel] ?? 0)
5517
- );
5518
- return {
5519
- ...coefficients,
5520
- skyIrradiance: [sky[0], sky[1], sky[2]],
5521
- diffuse,
5522
- finite: coefficients.finite && diffuse.every(Number.isFinite)
5523
- };
5524
- }
5525
- function vectorDistance(left, right) {
5526
- const dx = left[0] - right[0];
5527
- const dy = left[1] - right[1];
5528
- const dz = left[2] - right[2];
5529
- return Math.hypot(dx, dy, dz);
5530
- }
5531
- function encodeProbeRecord(objectKey, generation, localBlendFraction, shPreblend, candidate, accepted, lastKnownGood, sentinel) {
5532
- const values = new Float32Array(PROBE_BLEND_RECORD_BYTE_SIZE / Float32Array.BYTES_PER_ELEMENT);
5533
- values[0] = objectKey;
5534
- values[1] = generation;
5535
- values[2] = roundProbeToF32(localBlendFraction) ?? 0;
5536
- values[3] = (1 ) | (accepted ? 2 : 0) | (lastKnownGood ? 4 : 0);
5537
- for (let band = 0; band < 9; band += 1) {
5538
- const lane = 4 + band * 4;
5539
- values[lane] = roundProbeToF32(shPreblend[band * 3] ?? 0) ?? 0;
5540
- values[lane + 1] = roundProbeToF32(shPreblend[band * 3 + 1] ?? 0) ?? 0;
5541
- values[lane + 2] = roundProbeToF32(shPreblend[band * 3 + 2] ?? 0) ?? 0;
5542
- values[lane + 3] = 0;
5543
- }
5544
- const packedSh = new Array(27);
5545
- for (let band = 0; band < 9; band += 1) {
5546
- packedSh[band * 3] = values[4 + band * 4] ?? 0;
5547
- packedSh[band * 3 + 1] = values[5 + band * 4] ?? 0;
5548
- packedSh[band * 3 + 2] = values[6 + band * 4] ?? 0;
5549
- }
5550
6473
  return {
5551
- objectKey,
5552
- generation,
5553
- localBlendFraction: values[2] ?? 0,
5554
- shPreblend: packedSh,
5555
- bytes: new Uint8Array(values.buffer),
5556
- byteLength: values.byteLength,
5557
- candidate,
5558
- accepted,
5559
- lastKnownGood,
5560
- ...sentinel === void 0 ? {} : { sentinel }
6474
+ status: "available",
6475
+ ...validatedMembers[0],
6476
+ additional: validatedMembers.slice(1)
5561
6477
  };
5562
6478
  }
5563
- function sameList(left, right) {
5564
- return left.length === right.length && left.every((value, index) => value === right[index]);
5565
- }
5566
- function sameVector3(left, right) {
5567
- return left[0] === right[0] && left[1] === right[1] && left[2] === right[2];
5568
- }
5569
- function sameProbeFacts(left, right) {
5570
- if (left.length !== right.length) return false;
5571
- return left.every((probe, index) => {
5572
- const other = right[index];
5573
- if (other === void 0 || probe.identity !== other.identity || probe.radius !== other.radius || probe.admitted !== other.admitted || !sameVector3(probe.position, other.position) || probe.irradiance.length !== other.irradiance.length)
5574
- return false;
5575
- for (let value = 0; value < probe.irradiance.length; value += 1) {
5576
- if (probe.irradiance[value] !== other.irradiance[value]) return false;
5577
- }
5578
- return true;
5579
- });
5580
- }
5581
- function sameSkyFacts(left, right) {
5582
- const leftSky = left.sky;
5583
- const rightSky = right.sky;
5584
- if (leftSky === void 0 !== (rightSky === void 0)) return false;
5585
- if (leftSky !== void 0 && rightSky !== void 0 && (leftSky.available !== rightSky.available || leftSky.identity !== rightSky.identity || leftSky.sourceKey !== rightSky.sourceKey || leftSky.fallbackReason !== rightSky.fallbackReason || !sameVector3(leftSky.irradiance, rightSky.irradiance)))
5586
- return false;
5587
- if (left.skyIrradiance === void 0 !== (right.skyIrradiance === void 0)) return false;
5588
- return left.skyIrradiance === void 0 || right.skyIrradiance !== void 0 && sameVector3(left.skyIrradiance, right.skyIrradiance);
5589
- }
5590
- var ProbeBlendSceneProjection = class {
5591
- previous;
5592
- records = /* @__PURE__ */ new Map();
5593
- lastError;
5594
- lastReceipt = emptyReceipt(PROBE_R_MIN);
5595
- lastActiveContributorCount = 0;
5596
- lastAdmittedProbeCount = 0;
5597
- lastCoverage = 0;
5598
- lastFinite = true;
5599
- lastSky = {
5600
- available: false,
5601
- irradiance: [0, 0, 0],
5602
- fallbackReason: "no-skylight"
5603
- };
5604
- lastDirtyVisitCount = 0;
5605
- lastDirtyReasons = [];
5606
- recordFacts = /* @__PURE__ */ new Map();
5607
- getRecord(objectKey) {
5608
- return this.records.get(objectKey);
5609
- }
5610
- hasRecords() {
5611
- return this.records.size > 0;
5612
- }
5613
- inspect() {
5614
- return {
5615
- activeContributorCount: this.lastActiveContributorCount,
5616
- admittedProbeCount: this.lastAdmittedProbeCount,
5617
- coverage: this.lastCoverage,
5618
- skyResidualFraction: 1 - this.lastCoverage,
5619
- finite: this.lastFinite,
5620
- errorCode: this.lastError?.code,
5621
- records: [...this.records.values()].map((record) => {
5622
- const facts = this.recordFacts.get(record.objectKey);
5623
- return {
5624
- objectKey: record.objectKey,
5625
- generation: record.generation,
5626
- localBlendFraction: record.localBlendFraction,
5627
- shPreblend: [...record.shPreblend],
5628
- byteLength: record.byteLength,
5629
- candidate: record.candidate,
5630
- accepted: record.accepted,
5631
- lastKnownGood: record.lastKnownGood,
5632
- sentinel: record.sentinel,
5633
- probeBlendIndex: record.objectKey,
5634
- contributors: (facts?.contributors ?? []).map((term) => ({
5635
- identity: term.identity,
5636
- distance: term.distance,
5637
- radius: term.radius,
5638
- coverage: term.coverage,
5639
- q: term.q,
5640
- qHat: term.qHat,
5641
- alpha: term.alpha
5642
- })),
5643
- qHatSum: facts?.qHatSum ?? 0,
5644
- rStar: facts?.rStar ?? PROBE_R_MIN,
5645
- fallbackReason: facts?.fallbackReason
5646
- };
5647
- }),
5648
- sky: {
5649
- available: this.lastSky?.available ?? false,
5650
- identity: this.lastSky?.identity,
5651
- sourceKey: this.lastSky?.sourceKey,
5652
- irradiance: [...this.lastSky?.irradiance ?? [0, 0, 0]],
5653
- fallbackReason: this.lastSky?.fallbackReason
5654
- },
5655
- dirtyVisitCount: this.lastDirtyVisitCount,
5656
- dirtyReasons: [...this.lastDirtyReasons],
5657
- receipt: {
5658
- activeIdentities: [...this.lastReceipt.activeIdentities],
5659
- admittedIdentities: [...this.lastReceipt.admittedIdentities],
5660
- rejectedIdentities: [...this.lastReceipt.rejectedIdentities],
5661
- stableOrder: [...this.lastReceipt.stableOrder],
5662
- capacity: this.lastReceipt.capacity,
5663
- scaleRadius: this.lastReceipt.scaleRadius,
5664
- finite: this.lastReceipt.finite,
5665
- overflowReason: this.lastReceipt.overflowReason
5666
- }
5667
- };
5668
- }
5669
- apply(input) {
5670
- const dirtyReasons = [];
5671
- const affectedObjectKeys = /* @__PURE__ */ new Set();
5672
- const previous = this.previous;
5673
- const objectKeys = input.objects.map((object) => String(object.objectKey)).sort();
5674
- const previousObjectKeys = previous?.objects.map((object) => String(object.objectKey)).sort() ?? [];
5675
- if (previous === void 0 || !sameList(objectKeys, previousObjectKeys)) {
5676
- dirtyReasons.push("object-boundary");
5677
- for (const object of input.objects) affectedObjectKeys.add(object.objectKey);
5678
- }
5679
- if (previous !== void 0 && input.worldRevision !== previous.worldRevision)
5680
- dirtyReasons.push("world");
5681
- if (previous !== void 0 && !sameProbeFacts(input.probes, previous.probes))
5682
- dirtyReasons.push("probe-fact");
5683
- if (previous !== void 0 && !sameSkyFacts(input, previous)) dirtyReasons.push("sky");
5684
- const previousObjects = new Map(previous?.objects.map((object) => [object.objectKey, object]));
5685
- for (const object of input.objects) {
5686
- const oldObject = previousObjects.get(object.objectKey);
5687
- if (oldObject === void 0) {
5688
- affectedObjectKeys.add(object.objectKey);
5689
- continue;
5690
- }
5691
- if (!sameVector3(object.position, oldObject.position)) {
5692
- dirtyReasons.push("object-position");
5693
- affectedObjectKeys.add(object.objectKey);
5694
- }
5695
- if (object.generation !== oldObject.generation) {
5696
- dirtyReasons.push("generation");
5697
- affectedObjectKeys.add(object.objectKey);
5698
- }
5699
- }
5700
- if (dirtyReasons.includes("world") || dirtyReasons.includes("probe-fact") || dirtyReasons.includes("sky"))
5701
- for (const object of input.objects) affectedObjectKeys.add(object.objectKey);
5702
- const uniqueDirtyReasons = [...new Set(dirtyReasons)];
5703
- if (previous !== void 0 && uniqueDirtyReasons.length === 0) {
5704
- return {
5705
- records: [...this.records.values()],
5706
- contributors: [],
5707
- activeContributorCount: this.lastActiveContributorCount,
5708
- admittedProbeCount: this.lastAdmittedProbeCount,
5709
- coverage: this.lastCoverage,
5710
- skyResidualFraction: 1 - this.lastCoverage,
5711
- dirtyReasons: [],
5712
- affectedObjectKeys: [],
5713
- visits: 0,
5714
- allocations: 0,
5715
- uploads: 0,
5716
- receipt: this.lastReceipt,
5717
- ...this.lastError === void 0 ? {} : { error: this.lastError }
5718
- };
5719
- }
5720
- const nextRecords = new Map(this.records);
5721
- const currentObjectKeys = new Set(input.objects.map((object) => object.objectKey));
5722
- for (const objectKey of nextRecords.keys()) {
5723
- if (!currentObjectKeys.has(objectKey)) {
5724
- nextRecords.delete(objectKey);
5725
- this.recordFacts.delete(objectKey);
5726
- }
5727
- }
5728
- let firstTerms = [];
5729
- let firstReceipt = emptyReceipt(PROBE_R_MIN);
5730
- let firstError;
5731
- let firstActiveCount = 0;
5732
- let firstAdmittedCount = 0;
5733
- let firstCoverage = 0;
5734
- let firstObjectSeen = false;
5735
- let visited = 0;
5736
- for (const object of input.objects) {
5737
- if (!affectedObjectKeys.has(object.objectKey)) continue;
5738
- visited += 1;
5739
- const candidates = input.probes.map((probe) => ({
5740
- identity: probe.identity,
5741
- admitted: probe.admitted,
5742
- distance: vectorDistance(object.position, probe.position),
5743
- radius: probe.radius,
5744
- irradiance: probe.irradiance
5745
- }));
5746
- const admission = admitProbeContributors(candidates);
5747
- if (!firstObjectSeen) {
5748
- firstObjectSeen = true;
5749
- firstActiveCount = admission.active.length;
5750
- firstAdmittedCount = admission.admitted.length;
5751
- firstReceipt = admission.receipt;
5752
- }
5753
- const previousRecord = this.records.get(object.objectKey);
5754
- const lastKnownGood = input.lastKnownGood ?? previousRecord?.lastKnownGood ?? false;
5755
- let record;
5756
- if (admission.error !== void 0) firstError ??= admission.error;
5757
- if (admission.admitted.length === 0) {
5758
- if (input.probes.length === 0) {
5759
- nextRecords.delete(object.objectKey);
5760
- this.recordFacts.delete(object.objectKey);
5761
- continue;
5762
- }
5763
- const sentinel = lastKnownGood ? "no-active" : "no-lkg";
5764
- record = encodeProbeRecord(
5765
- object.objectKey,
5766
- object.generation,
5767
- 0,
5768
- new Array(27).fill(0),
5769
- true,
5770
- false,
5771
- lastKnownGood,
5772
- sentinel
5773
- );
5774
- this.recordFacts.set(object.objectKey, {
5775
- contributors: [],
5776
- qHatSum: 0,
5777
- rStar: PROBE_R_MIN,
5778
- fallbackReason: admission.error?.code ?? sentinel
5779
- });
5780
- } else {
5781
- const blend = blendProbeCoefficients(admission.admitted, PROBE_R_MIN);
5782
- if (blend === void 0) {
5783
- firstError ??= {
5784
- code: "invalid-admitted-prefix",
5785
- activeCount: admission.active.length,
5786
- capacity: PROBE_MAX_CONTRIBUTORS,
5787
- receipt: admission.receipt
5788
- };
5789
- firstReceipt = admission.receipt;
5790
- continue;
5791
- }
5792
- record = encodeProbeRecord(
5793
- object.objectKey,
5794
- object.generation,
5795
- blend.C,
5796
- blend.SHPreblend,
5797
- true,
5798
- true,
5799
- true,
5800
- void 0
5801
- );
5802
- this.recordFacts.set(object.objectKey, {
5803
- contributors: blend.terms,
5804
- qHatSum: blend.Q,
5805
- rStar: blend.rStar,
5806
- fallbackReason: admission.error === void 0 ? void 0 : "capacity-exceeded-stable-prefix"
5807
- });
5808
- if (firstTerms.length === 0) firstTerms = blend.terms;
5809
- firstCoverage = blend.C;
5810
- }
5811
- nextRecords.set(object.objectKey, record);
5812
- firstError ??= admission.error;
5813
- }
5814
- const allocations = previous === void 0 ? nextRecords.size : 0;
5815
- const result = {
5816
- records: [...nextRecords.values()],
5817
- contributors: firstTerms,
5818
- activeContributorCount: firstActiveCount,
5819
- admittedProbeCount: firstAdmittedCount,
5820
- coverage: firstCoverage,
5821
- skyResidualFraction: 1 - firstCoverage,
5822
- dirtyReasons: uniqueDirtyReasons,
5823
- affectedObjectKeys: [...affectedObjectKeys].sort((left, right) => left - right),
5824
- visits: visited,
5825
- allocations,
5826
- uploads: nextRecords.size,
5827
- receipt: firstReceipt,
5828
- ...firstError === void 0 ? {} : { error: firstError }
5829
- };
5830
- this.previous = input;
5831
- this.records = nextRecords;
5832
- this.lastError = firstError;
5833
- this.lastReceipt = firstReceipt;
5834
- this.lastActiveContributorCount = firstActiveCount;
5835
- this.lastAdmittedProbeCount = firstAdmittedCount;
5836
- this.lastCoverage = firstCoverage;
5837
- this.lastFinite = [...nextRecords.values()].every(
5838
- (record) => record.bytes.length === PROBE_BLEND_RECORD_BYTE_SIZE
5839
- );
5840
- this.lastSky = input.sky ?? (input.skyIrradiance === void 0 ? { available: false, irradiance: [0, 0, 0], fallbackReason: "no-skylight" } : { available: true, irradiance: input.skyIrradiance });
5841
- this.lastDirtyVisitCount = visited;
5842
- this.lastDirtyReasons = uniqueDirtyReasons;
5843
- return result;
5844
- }
5845
- };
5846
6479
 
5847
- export { InstanceBoundsCache, OcclusionRenderRuntime, PROBE_MAX_CONTRIBUTORS, ProbeBlendSceneProjection, SSR_HISTORY_FORMAT, SsrHistoryOwner, admitProbeContributors, blendLightProbes, buildRectAreaWorldFrame, buildShadowFrusta, createCookieProjectionMatrixData, createExtendedLightingState, createSsrHistoryOwner, createSsrSpatialResources, emptyBloomInspection, estimateSsrSpatialMemory, extractFrames, fingerprintNumericArray, glyphTextLayoutSystem, primitiveKey, primitiveKeyId, projectExtendedLightingInspection, promoteExtendedLightingCandidate, recordExtendedLightingFailure, rectAreaFacesPoint, scaledProbeWeight, setOcclusionRuntimeTestHooks, viewKey, viewKeyId };
5848
- //# sourceMappingURL=chunk-OO3DRYK7.mjs.map
5849
- //# sourceMappingURL=chunk-OO3DRYK7.mjs.map
6480
+ export { ANTIALIAS_FXAA, ANTIALIAS_MSAA, ANTIALIAS_NONE, ANTIALIAS_TAA, Atmosphere, BLOOM_DISABLED, BLOOM_ENABLED, BarrelDistortion, CAMERA_BLOOM_INTENSITY_MAX, CAMERA_BLOOM_INTENSITY_MIN, CAMERA_BLOOM_SCATTER_MAX, CAMERA_BLOOM_SCATTER_MIN, CAMERA_BLOOM_SOFT_KNEE_MAX, CAMERA_BLOOM_SOFT_KNEE_MIN, CAMERA_BLOOM_THRESHOLD_MAX, CAMERA_BLOOM_THRESHOLD_MIN, CAMERA_EXPOSURE_MODE_AUTO, CAMERA_EXPOSURE_MODE_MANUAL, CAMERA_PROJECTION_ORTHOGRAPHIC, CAMERA_PROJECTION_PERSPECTIVE, CAMERA_TEMPERATURE_MAX, CAMERA_TEMPERATURE_MIN, CAMERA_TINT_MAX, CAMERA_TINT_MIN, CLOUD_EXTINCTION_COEFFICIENT, CLOUD_QUALITY_PROFILES, CUBE_CAMERA_FACE_ORDER, CUBE_CAMERA_UPDATE_CONTINUOUS, CUBE_CAMERA_UPDATE_ONCE, CUBE_CAMERA_UPDATE_ON_DEMAND, Camera, CameraError, CloudLayer, CloudLayerCacheInvalidError, CloudLayerCapabilityMissingError, CloudLayerInvalidParameterError, CloudLayerOwnerConflictError, CloudLayerResourceFailureError, CloudQualityValue, CubeCamera, DEFAULT_CLOUD_LAYER, DEFAULT_DEPTH_OF_FIELD_PARAMS, DEPTH_OF_FIELD_PARAMS_BYTE_SIZE, DepthOfField, DepthOfFieldQualityValue, DepthOfFieldSideValue, DepthOfFieldValidationError, DirectionalLight, DirectionalShadowFilterValue, DynamicResolution, Fog, InstanceBoundsCache, Instances, Layer, LightProbe, Lines, MAX_VOLUMETRIC_FOG_OWNERS, MeshFilter, MeshRenderer, MotionBlur, PointLight, PointLightShadow, PointShapeValue, Points, PostProcessParams, REFLECTION_PROBE_UPDATE_CONTINUOUS, REFLECTION_PROBE_UPDATE_ONCE, REFLECTION_PROBE_UPDATE_ON_CHANGE, RectAreaLight, ReflectionProbe, SKYBOX_MODE_CUBEMAP, SKYLIGHT_RECOVERY_FALLBACK, SceneInstance, ScreenSpaceReflection, SkyboxBackground, Skylight, SortKey, SpotLight, SunCardinalityError, TONEMAP_ACES_FILMIC, TONEMAP_AGX, TONEMAP_CINEON, TONEMAP_LINEAR, TONEMAP_NEUTRAL, TONEMAP_NONE, TONEMAP_REINHARD, TONEMAP_REINHARD_EXTENDED, Visibility, VisibilityStateValue, VolumetricFog, VolumetricFogSamplingValue, attachBarrelDistortionCameraFrame, buildCubeCameraFaceViews, buildRectAreaWorldFrame, buildShadowFrusta, cameraExposureFromColumns, cameraProjectionFromF32, cloudLayerFormationKey, cloudLayerSourceKey, cloudQualityFromF32, cloudShadowResolutionForQuality, cloudViewDistanceForQuality, computeInvRangeSquared, createBarrelDistortionMapping, createCookieProjectionMatrixData, cubeCameraUpdateIntentFromF32, cubeCameraUpdateIntentToF32, degToCos, depthOfFieldQualityCode, depthOfFieldQualityFromF32, depthOfFieldRequestFailure, depthOfFieldSideCode, depthOfFieldSideFromF32, depthOfFieldTapCount, directionalShadowQualityFromF32, extractCloudLayer, extractFrames, extractVolumetricFog, fingerprintNumericArray, freezeBarrelDistortionMapping, glyphTextLayoutSystem, hasVolumetricFogCapability, internSharedRefFromGuid, mapDisplayToScene, mapDisplayUvToSceneUv, mapSceneToDisplay, mapSceneUvToDisplayUv, orthographic, packDepthOfFieldParams, perspective, pointShapeFromU32, projectMeshMaterialBindingObservation, rectAreaFacesPoint, reflectionProbeUpdateIntentFromF32, reflectionProbeUpdateIntentToF32, resolveDepthOfFieldFrameParams, resolveDepthOfFieldParams, resolveIntegratedVolumeConsumer, resolveMaterialSnapshot, resolveSelectedVolumetricLight, resolveVisibility, resolveVolumetricFogLightPair, selectCloudLayerFrame, signedDepthOfFieldCoC, summarizeMeshMaterialBindings, validateBarrelDistortionParameters, validateCameraBloom, validateCameraColorGrading, validateCameraExposure, validateCloudLayer, validateDepthOfFieldFrameParams, validateDepthOfFieldParams, validateDirection, validateDirectionalLightData, validateDynamicResolutionCamera, validateDynamicResolutionParameters, validateLightProbeData, validatePointLightData, validatePointLightShadowData, validateRectAreaLightData, validateSpotLightData, validateVolumetricFog, visibilityStateFromU32 };
6481
+ //# sourceMappingURL=chunk-G6HDNGY7.mjs.map
6482
+ //# sourceMappingURL=chunk-G6HDNGY7.mjs.map