@forgeax/engine-render 0.1.19 → 0.1.21

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 (452) hide show
  1. package/README.md +215 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/assembly/device-feature-admission.d.ts +18 -0
  4. package/dist/assembly/device-feature-admission.d.ts.map +1 -0
  5. package/dist/assembly/directional-shadow-inspection.d.ts +38 -0
  6. package/dist/assembly/directional-shadow-inspection.d.ts.map +1 -0
  7. package/dist/assembly/factory.d.ts +34 -20
  8. package/dist/assembly/factory.d.ts.map +1 -1
  9. package/dist/assembly/host-contract.d.ts +9 -0
  10. package/dist/assembly/host-contract.d.ts.map +1 -1
  11. package/dist/assembly/render-target-host.d.ts +46 -0
  12. package/dist/assembly/render-target-host.d.ts.map +1 -0
  13. package/dist/assets/render-asset-port.d.ts.map +1 -1
  14. package/dist/authoring.mjs +3 -0
  15. package/dist/authoring.mjs.map +1 -1
  16. package/dist/capture/cube-views.d.ts +21 -0
  17. package/dist/capture/cube-views.d.ts.map +1 -0
  18. package/dist/capture/scheduler.d.ts +54 -0
  19. package/dist/capture/scheduler.d.ts.map +1 -0
  20. package/dist/{chunk-RJRUS55L.mjs → chunk-5YADUCA7.mjs} +1896 -1624
  21. package/dist/chunk-5YADUCA7.mjs.map +1 -0
  22. package/dist/{chunk-LOSO4CTV.mjs → chunk-6OPXDQ2E.mjs} +423 -46
  23. package/dist/chunk-6OPXDQ2E.mjs.map +1 -0
  24. package/dist/{chunk-IPXCABEP.mjs → chunk-BXBQQKFQ.mjs} +3 -9
  25. package/dist/chunk-BXBQQKFQ.mjs.map +1 -0
  26. package/dist/chunk-FS2KNUQH.mjs +1294 -0
  27. package/dist/chunk-FS2KNUQH.mjs.map +1 -0
  28. package/dist/components/camera.d.ts +1 -0
  29. package/dist/components/camera.d.ts.map +1 -1
  30. package/dist/components/cube-camera.d.ts +20 -0
  31. package/dist/components/cube-camera.d.ts.map +1 -0
  32. package/dist/components/directional-light.d.ts +11 -2
  33. package/dist/components/directional-light.d.ts.map +1 -1
  34. package/dist/components/directional-shadow-filter.d.ts +25 -0
  35. package/dist/components/directional-shadow-filter.d.ts.map +1 -0
  36. package/dist/components/index.d.ts +2 -0
  37. package/dist/components/index.d.ts.map +1 -1
  38. package/dist/components/light-helpers.d.ts.map +1 -1
  39. package/dist/components/reflection-probe.d.ts +17 -0
  40. package/dist/components/reflection-probe.d.ts.map +1 -0
  41. package/dist/components/spot-light.d.ts +12 -0
  42. package/dist/components/spot-light.d.ts.map +1 -1
  43. package/dist/construct-renderer.mjs +8552 -1612
  44. package/dist/construct-renderer.mjs.map +1 -1
  45. package/dist/device/gpu-residency.d.ts +19 -2
  46. package/dist/device/gpu-residency.d.ts.map +1 -1
  47. package/dist/environment/frame.d.ts.map +1 -1
  48. package/dist/environment/lifecycle.d.ts +5 -0
  49. package/dist/environment/lifecycle.d.ts.map +1 -1
  50. package/dist/errors/render.d.ts +209 -93
  51. package/dist/errors/render.d.ts.map +1 -1
  52. package/dist/extract/directional-shadow-projection.d.ts +23 -0
  53. package/dist/extract/directional-shadow-projection.d.ts.map +1 -0
  54. package/dist/extract/environment.d.ts +14 -0
  55. package/dist/extract/environment.d.ts.map +1 -1
  56. package/dist/extract/visibility.d.ts.map +1 -1
  57. package/dist/features/fullscreen.d.ts.map +1 -1
  58. package/dist/features/targets.d.ts.map +1 -1
  59. package/dist/features/types.d.ts +10 -0
  60. package/dist/features/types.d.ts.map +1 -1
  61. package/dist/fullscreen-post-process-pass.d.ts +13 -2
  62. package/dist/fullscreen-post-process-pass.d.ts.map +1 -1
  63. package/dist/gpu-driven/batch-topology.d.ts.map +1 -1
  64. package/dist/gpu-driven/production-raster.d.ts +0 -1
  65. package/dist/gpu-driven/production-raster.d.ts.map +1 -1
  66. package/dist/gpu-scene.d.ts.map +1 -1
  67. package/dist/gpu-usage.d.ts +2 -0
  68. package/dist/gpu-usage.d.ts.map +1 -1
  69. package/dist/hdrp-buffers.d.ts +19 -17
  70. package/dist/hdrp-buffers.d.ts.map +1 -1
  71. package/dist/ibl/IblPipelineCache.d.ts +4 -0
  72. package/dist/ibl/IblPipelineCache.d.ts.map +1 -1
  73. package/dist/ibl/kernel-cache.d.ts +36 -0
  74. package/dist/ibl/kernel-cache.d.ts.map +1 -0
  75. package/dist/index.d.ts +35 -4
  76. package/dist/index.d.ts.map +1 -1
  77. package/dist/index.mjs +4 -4
  78. package/dist/index.mjs.map +1 -1
  79. package/dist/inspection-types.d.ts +51 -0
  80. package/dist/inspection-types.d.ts.map +1 -1
  81. package/dist/instances-derived-bounds.d.ts +50 -0
  82. package/dist/instances-derived-bounds.d.ts.map +1 -0
  83. package/dist/internal.mjs +1 -1
  84. package/dist/light-buffer-layout.d.ts +30 -13
  85. package/dist/light-buffer-layout.d.ts.map +1 -1
  86. package/dist/mesh-material-bindings.d.ts +54 -0
  87. package/dist/mesh-material-bindings.d.ts.map +1 -1
  88. package/dist/pbr-pipeline.d.ts +20 -7
  89. package/dist/pbr-pipeline.d.ts.map +1 -1
  90. package/dist/pipeline/standard-forward-lane.d.ts +2 -1
  91. package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
  92. package/dist/pipeline/standard-lighting/errors.d.ts +4 -0
  93. package/dist/pipeline/standard-lighting/errors.d.ts.map +1 -0
  94. package/dist/pipeline/standard-lighting/graph.d.ts +24 -0
  95. package/dist/pipeline/standard-lighting/graph.d.ts.map +1 -0
  96. package/dist/pipeline/standard-lighting/inspection.d.ts +38 -0
  97. package/dist/pipeline/standard-lighting/inspection.d.ts.map +1 -0
  98. package/dist/pipeline/standard-lighting/layout.d.ts +28 -0
  99. package/dist/pipeline/standard-lighting/layout.d.ts.map +1 -0
  100. package/dist/pipeline/standard-lighting/light-frame.d.ts +33 -0
  101. package/dist/pipeline/standard-lighting/light-frame.d.ts.map +1 -0
  102. package/dist/pipeline/standard-lighting/membership.d.ts +14 -0
  103. package/dist/pipeline/standard-lighting/membership.d.ts.map +1 -0
  104. package/dist/pipeline/standard-lighting/prepare.d.ts +16 -0
  105. package/dist/pipeline/standard-lighting/prepare.d.ts.map +1 -0
  106. package/dist/pipeline/standard-lighting/topology.d.ts +45 -0
  107. package/dist/pipeline/standard-lighting/topology.d.ts.map +1 -0
  108. package/dist/pipeline/standard-lighting/transport.d.ts +36 -0
  109. package/dist/pipeline/standard-lighting/transport.d.ts.map +1 -0
  110. package/dist/pipeline/standard-pipeline.d.ts +4 -13
  111. package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
  112. package/dist/pipeline/standard-post.d.ts.map +1 -1
  113. package/dist/pipeline/standard-profile.d.ts +12 -20
  114. package/dist/pipeline/standard-profile.d.ts.map +1 -1
  115. package/dist/pipeline-spec.d.ts +40 -7
  116. package/dist/pipeline-spec.d.ts.map +1 -1
  117. package/dist/point-shadow-inspection.d.ts +21 -0
  118. package/dist/point-shadow-inspection.d.ts.map +1 -0
  119. package/dist/record/frame-lighting.d.ts +29 -32
  120. package/dist/record/frame-lighting.d.ts.map +1 -1
  121. package/dist/record/frame-snapshot.d.ts +65 -15
  122. package/dist/record/frame-snapshot.d.ts.map +1 -1
  123. package/dist/record/frame-targets.d.ts +5 -0
  124. package/dist/record/frame-targets.d.ts.map +1 -1
  125. package/dist/record/frame.d.ts +12 -5
  126. package/dist/record/frame.d.ts.map +1 -1
  127. package/dist/record/gpu-pass-timing/contract.d.ts +96 -0
  128. package/dist/record/gpu-pass-timing/contract.d.ts.map +1 -0
  129. package/dist/record/gpu-pass-timing/errors.d.ts +18 -0
  130. package/dist/record/gpu-pass-timing/errors.d.ts.map +1 -0
  131. package/dist/record/gpu-pass-timing/index.d.ts +8 -0
  132. package/dist/record/gpu-pass-timing/index.d.ts.map +1 -0
  133. package/dist/record/gpu-pass-timing/parser.d.ts +11 -0
  134. package/dist/record/gpu-pass-timing/parser.d.ts.map +1 -0
  135. package/dist/record/gpu-pass-timing/session.d.ts +43 -0
  136. package/dist/record/gpu-pass-timing/session.d.ts.map +1 -0
  137. package/dist/record/gpu-timing.d.ts +37 -0
  138. package/dist/record/gpu-timing.d.ts.map +1 -0
  139. package/dist/record/main-pass-geometry.d.ts.map +1 -1
  140. package/dist/record/main-pass-material.d.ts +23 -2
  141. package/dist/record/main-pass-material.d.ts.map +1 -1
  142. package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
  143. package/dist/record/main-pass.d.ts +2 -0
  144. package/dist/record/main-pass.d.ts.map +1 -1
  145. package/dist/record/render-context.d.ts +48 -3
  146. package/dist/record/render-context.d.ts.map +1 -1
  147. package/dist/record/shadow-pass.d.ts.map +1 -1
  148. package/dist/record/skybox-post-pass.d.ts.map +1 -1
  149. package/dist/record/typed-frame-graph.d.ts +81 -5
  150. package/dist/record/typed-frame-graph.d.ts.map +1 -1
  151. package/dist/record/view-ubo.d.ts +7 -4
  152. package/dist/record/view-ubo.d.ts.map +1 -1
  153. package/dist/reflection/filter.d.ts +22 -0
  154. package/dist/reflection/filter.d.ts.map +1 -0
  155. package/dist/reflection/gpu-table.d.ts +25 -0
  156. package/dist/reflection/gpu-table.d.ts.map +1 -0
  157. package/dist/reflection/projection.d.ts +91 -0
  158. package/dist/reflection/projection.d.ts.map +1 -0
  159. package/dist/render-contract.d.ts +114 -7
  160. package/dist/render-contract.d.ts.map +1 -1
  161. package/dist/render-data.d.ts +10 -6
  162. package/dist/render-data.d.ts.map +1 -1
  163. package/dist/render-graph-primitives.d.ts +1 -0
  164. package/dist/render-graph-primitives.d.ts.map +1 -1
  165. package/dist/render-pipeline.d.ts +78 -2
  166. package/dist/render-pipeline.d.ts.map +1 -1
  167. package/dist/render-system-extract.d.ts +113 -20
  168. package/dist/render-system-extract.d.ts.map +1 -1
  169. package/dist/render-system.d.ts +24 -3
  170. package/dist/render-system.d.ts.map +1 -1
  171. package/dist/renderer-inspect.d.ts +29 -0
  172. package/dist/renderer-inspect.d.ts.map +1 -0
  173. package/dist/renderer.d.ts +43 -0
  174. package/dist/renderer.d.ts.map +1 -0
  175. package/dist/scene/render-scene.d.ts +14 -0
  176. package/dist/scene/render-scene.d.ts.map +1 -1
  177. package/dist/shadow-atlas.d.ts +3 -3
  178. package/dist/shadow-atlas.d.ts.map +1 -1
  179. package/dist/ssao-buffers.d.ts +3 -0
  180. package/dist/ssao-buffers.d.ts.map +1 -1
  181. package/dist/targets/contracts.d.ts +66 -0
  182. package/dist/targets/contracts.d.ts.map +1 -0
  183. package/dist/targets/graph-projection.d.ts +29 -0
  184. package/dist/targets/graph-projection.d.ts.map +1 -0
  185. package/dist/targets/material-source.d.ts +34 -0
  186. package/dist/targets/material-source.d.ts.map +1 -0
  187. package/dist/targets/owner.d.ts +32 -0
  188. package/dist/targets/owner.d.ts.map +1 -0
  189. package/dist/targets/physical.d.ts +18 -0
  190. package/dist/targets/physical.d.ts.map +1 -0
  191. package/dist/targets/readback.d.ts +39 -0
  192. package/dist/targets/readback.d.ts.map +1 -0
  193. package/dist/temporal/frame.d.ts +44 -0
  194. package/dist/temporal/frame.d.ts.map +1 -0
  195. package/dist/temporal/index.mjs +3 -3
  196. package/dist/typed-render-graph-primitives.d.ts +6 -1
  197. package/dist/typed-render-graph-primitives.d.ts.map +1 -1
  198. package/dist/typed-shadow-atlas.d.ts +10 -0
  199. package/dist/typed-shadow-atlas.d.ts.map +1 -0
  200. package/dist/typed-shadow-passes.d.ts +1 -1
  201. package/dist/typed-shadow-passes.d.ts.map +1 -1
  202. package/dist/volume/capability.d.ts +57 -0
  203. package/dist/volume/capability.d.ts.map +1 -0
  204. package/dist/volume/component.d.ts +61 -0
  205. package/dist/volume/component.d.ts.map +1 -0
  206. package/dist/volume/extract.d.ts +11 -0
  207. package/dist/volume/extract.d.ts.map +1 -0
  208. package/dist/volume/inspection.d.ts +38 -0
  209. package/dist/volume/inspection.d.ts.map +1 -0
  210. package/dist/volume/passes.d.ts +38 -0
  211. package/dist/volume/passes.d.ts.map +1 -0
  212. package/dist/volume/recovery.d.ts +38 -0
  213. package/dist/volume/recovery.d.ts.map +1 -0
  214. package/dist/volume/resources.d.ts +57 -0
  215. package/dist/volume/resources.d.ts.map +1 -0
  216. package/dist/volume/temporal.d.ts +35 -0
  217. package/dist/volume/temporal.d.ts.map +1 -0
  218. package/package.json +20 -20
  219. package/src/__tests__/bloom-exact-zero.unit.test.ts +48 -19
  220. package/src/__tests__/bloom-lifecycle.integration.test.ts +41 -13
  221. package/src/__tests__/camera-target-consumption.unit.test.ts +53 -0
  222. package/src/__tests__/cluster-binner.unit.test.ts +35 -0
  223. package/src/__tests__/cluster-grid-default-surface.unit.test.ts +7 -2
  224. package/src/__tests__/cube-camera-scheduler.unit.test.ts +70 -0
  225. package/src/__tests__/cube-camera-single-submit.perf.test.ts +75 -0
  226. package/src/__tests__/cube-camera-view-matrices.unit.test.ts +41 -0
  227. package/src/__tests__/cube-camera-vocabulary.unit.test.ts +47 -0
  228. package/src/__tests__/cube-camera.integration.test.ts +42 -0
  229. package/src/__tests__/device-feature-admission.unit.test.ts +73 -0
  230. package/src/__tests__/direct-light-snapshot.unit.test.ts +3 -3
  231. package/src/__tests__/docs-gate-green.unit.test.ts +38 -0
  232. package/src/__tests__/factory-contract.integration.test.ts +255 -29
  233. package/src/__tests__/fog-component.unit.test.ts +67 -0
  234. package/src/__tests__/fog.integration.test.ts +86 -0
  235. package/src/__tests__/fullscreen-feature-plan.unit.test.ts +37 -0
  236. package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +10 -9
  237. package/src/__tests__/gpu-driven-indirect-raster.dawn.test.ts +17 -1
  238. package/src/__tests__/gpu-driven-production.integration.test.ts +16 -17
  239. package/src/__tests__/gpu-driven-view-graph.integration.test.ts +12 -4
  240. package/src/__tests__/gpu-pass-timing-browser-runner.ts +281 -0
  241. package/src/__tests__/gpu-pass-timing-coexistence.integration.test.ts +80 -0
  242. package/src/__tests__/gpu-pass-timing-contract.unit.test.ts +243 -0
  243. package/src/__tests__/gpu-pass-timing-exact-zero.unit.test.ts +131 -0
  244. package/src/__tests__/gpu-pass-timing-lifecycle.integration.test.ts +230 -0
  245. package/src/__tests__/gpu-pass-timing-observation.unit.test.ts +233 -0
  246. package/src/__tests__/gpu-pass-timing-session.unit.test.ts +438 -0
  247. package/src/__tests__/gpu-pass-timing.browser.test.ts +26 -0
  248. package/src/__tests__/gpu-pass-timing.rhi-null.unit.test.ts +125 -0
  249. package/src/__tests__/hdrp-group2-visibility.unit.test.ts +24 -0
  250. package/src/__tests__/instances-culling.integration.test.ts +94 -0
  251. package/src/__tests__/instances-derived-bounds.unit.test.ts +118 -0
  252. package/src/__tests__/ktx2-basis-gpu-consumer.dawn.test.ts +14 -14
  253. package/src/__tests__/material-observation.integration.test.ts +101 -0
  254. package/src/__tests__/mesh-buffer-usage-surface.unit.test.ts +1 -1
  255. package/src/__tests__/pbr-view-layout.unit.test.ts +16 -0
  256. package/src/__tests__/point-shadow-inspection.unit.test.ts +60 -0
  257. package/src/__tests__/public-surface.test-d.ts +10 -0
  258. package/src/__tests__/reflection-probe-authoring.unit.test.ts +71 -0
  259. package/src/__tests__/reflection-probe-filter.unit.test.ts +54 -0
  260. package/src/__tests__/reflection-probe-ibl-topology.unit.test.ts +45 -0
  261. package/src/__tests__/reflection-probe-pass-encode.integration.test.ts +101 -0
  262. package/src/__tests__/reflection-probe-performance.perf.test.ts +60 -0
  263. package/src/__tests__/reflection-probe-recovery.integration.test.ts +55 -0
  264. package/src/__tests__/reflection-probe-selection.unit.test.ts +46 -0
  265. package/src/__tests__/render-error-code-owner.test-d.ts +23 -3
  266. package/src/__tests__/render-feature-docs.unit.test.ts +1 -1
  267. package/src/__tests__/render-target-admission.unit.test.ts +80 -0
  268. package/src/__tests__/render-target-graph-projection.unit.test.ts +102 -0
  269. package/src/__tests__/render-target-inspection.unit.test.ts +52 -0
  270. package/src/__tests__/render-target-material-source.test-d.ts +30 -0
  271. package/src/__tests__/render-target-material-source.unit.test.ts +97 -0
  272. package/src/__tests__/render-target-owner-placement.unit.test.ts +203 -0
  273. package/src/__tests__/render-target-owner.unit.test.ts +122 -0
  274. package/src/__tests__/render-target-probe-soak.perf.test.ts +94 -0
  275. package/src/__tests__/render-target-public-schema.json +107 -0
  276. package/src/__tests__/render-target-public-schema.unit.test.ts +91 -0
  277. package/src/__tests__/render-target-public-surface.test-d.ts +83 -0
  278. package/src/__tests__/render-target-readback.integration.test.ts +90 -0
  279. package/src/__tests__/render-target-recovery.integration.test.ts +85 -0
  280. package/src/__tests__/renderer-factory-material-contract.unit.test.ts +76 -0
  281. package/src/__tests__/renderer-frame-transaction.integration.test.ts +1 -1
  282. package/src/__tests__/renderer-lifecycle.integration.test.ts +14 -0
  283. package/src/__tests__/shader-stage-surface.unit.test.ts +19 -4
  284. package/src/__tests__/skin-motion-regression.unit.test.ts +132 -0
  285. package/src/__tests__/skinned-shadow-caster.test.ts +32 -2
  286. package/src/__tests__/standard-cluster-capability.integration.test.ts +63 -0
  287. package/src/__tests__/standard-cluster-transport.unit.test.ts +121 -0
  288. package/src/__tests__/standard-cluster-variant-resolution.unit.test.ts +232 -0
  289. package/src/__tests__/standard-clustered-pipeline.unit.test.ts +8 -5
  290. package/src/__tests__/standard-lighting-inspection.integration.test.ts +138 -0
  291. package/src/__tests__/standard-lighting-ssot.unit.test.ts +18 -0
  292. package/src/__tests__/standard-lighting-write.unit.test.ts +377 -0
  293. package/src/__tests__/standard-lighting.integration.test.ts +67 -0
  294. package/src/__tests__/standard-pipeline.dawn.test.ts +206 -5
  295. package/src/__tests__/standard-pipeline.integration.test.ts +181 -150
  296. package/src/__tests__/standard-probe-binding.unit.test.ts +25 -0
  297. package/src/__tests__/standard-profile-oracle.unit.test.ts +23 -36
  298. package/src/__tests__/typed-pipeline-topology.unit.test.ts +395 -14
  299. package/src/__tests__/urp-pipeline-targets.unit.test.ts +2 -2
  300. package/src/__tests__/visibility-resolver.unit.test.ts +15 -0
  301. package/src/__tests__/volume-optics-oracle.unit.test.ts +85 -0
  302. package/src/__tests__/volumetric-fog-budget-recovery.integration.test.ts +655 -0
  303. package/src/__tests__/volumetric-fog-component.unit.test.ts +71 -0
  304. package/src/__tests__/volumetric-fog-composite-stage-readback.dawn.test.ts +376 -0
  305. package/src/__tests__/volumetric-fog-errors.unit.test.ts +51 -0
  306. package/src/__tests__/volumetric-fog-inspection.unit.test.ts +77 -0
  307. package/src/__tests__/volumetric-fog-light-capability.unit.test.ts +19 -0
  308. package/src/__tests__/volumetric-fog-light-consumers.integration.test.ts +18 -0
  309. package/src/__tests__/volumetric-fog-light-errors.unit.test.ts +17 -0
  310. package/src/__tests__/volumetric-fog-light-topology.unit.test.ts +21 -0
  311. package/src/__tests__/volumetric-fog-light-transaction.integration.test.ts +53 -0
  312. package/src/__tests__/volumetric-fog-m9-watcher.integration.test.ts +27 -0
  313. package/src/__tests__/volumetric-fog-point-light-owner.integration.test.ts +44 -0
  314. package/src/__tests__/volumetric-fog-point-spot-accumulation.integration.test.ts +49 -0
  315. package/src/__tests__/volumetric-fog-point-spot-contract.unit.test.ts +28 -0
  316. package/src/__tests__/volumetric-fog-point-spot-lkg.integration.test.ts +65 -0
  317. package/src/__tests__/volumetric-fog-point-spot-selection.integration.test.ts +49 -0
  318. package/src/__tests__/volumetric-fog-point-spot-temporal.unit.test.ts +55 -0
  319. package/src/__tests__/volumetric-fog-projector-contract.unit.test.ts +22 -0
  320. package/src/__tests__/volumetric-fog-projector-inspection.unit.test.ts +12 -0
  321. package/src/__tests__/volumetric-fog-projector-surface-volume.integration.test.ts +11 -0
  322. package/src/__tests__/volumetric-fog-projector-transaction.integration.test.ts +26 -0
  323. package/src/__tests__/volumetric-fog-recovery.integration.test.ts +47 -0
  324. package/src/__tests__/volumetric-fog-selected-light-resolution.integration.test.ts +63 -0
  325. package/src/__tests__/volumetric-fog-spot-closure.integration.test.ts +26 -0
  326. package/src/__tests__/volumetric-fog-spot-contract.test-d.ts +21 -0
  327. package/src/__tests__/volumetric-fog-spot-contract.unit.test.ts +27 -0
  328. package/src/__tests__/volumetric-fog-spot-frame-optics.integration.test.ts +14 -0
  329. package/src/__tests__/volumetric-fog-spot-optics.integration.test.ts +29 -0
  330. package/src/__tests__/volumetric-fog-stage-readback.dawn.test.ts +550 -0
  331. package/src/__tests__/volumetric-fog-temporal-selected-light.unit.test.ts +33 -0
  332. package/src/__tests__/volumetric-fog-temporal.unit.test.ts +45 -0
  333. package/src/__tests__/volumetric-fog-topology.unit.test.ts +237 -0
  334. package/src/__tests__/volumetric-fog-transparent.integration.test.ts +27 -0
  335. package/src/assembly/device-feature-admission.ts +73 -0
  336. package/src/assembly/directional-shadow-inspection.ts +122 -0
  337. package/src/assembly/factory.ts +1093 -246
  338. package/src/assembly/host-contract.ts +25 -0
  339. package/src/assembly/render-target-host.ts +847 -0
  340. package/src/assets/asset-decoders.ts +1 -1
  341. package/src/assets/render-asset-port.ts +7 -1
  342. package/src/capture/cube-views.ts +69 -0
  343. package/src/capture/scheduler.ts +265 -0
  344. package/src/components/__tests__/schema-contract.unit.test.ts +12 -0
  345. package/src/components/__tests__/schema-parity.unit.test.ts +9 -0
  346. package/src/components/camera.ts +4 -0
  347. package/src/components/cube-camera.ts +52 -0
  348. package/src/components/directional-light.ts +22 -2
  349. package/src/components/directional-shadow-filter.ts +57 -0
  350. package/src/components/index.ts +2 -0
  351. package/src/components/light-helpers.ts +85 -7
  352. package/src/components/reflection-probe.ts +49 -0
  353. package/src/components/spot-light.ts +22 -3
  354. package/src/device/gpu-residency.ts +168 -58
  355. package/src/environment/frame.ts +6 -24
  356. package/src/environment/lifecycle.ts +12 -0
  357. package/src/errors/render.ts +405 -143
  358. package/src/extract/directional-shadow-projection.ts +61 -0
  359. package/src/extract/environment.ts +57 -0
  360. package/src/extract/visibility.ts +25 -1
  361. package/src/features/fullscreen.ts +3 -0
  362. package/src/features/targets.ts +4 -1
  363. package/src/features/types.ts +10 -0
  364. package/src/fullscreen-post-process-pass.ts +43 -2
  365. package/src/gpu-driven/batch-topology.ts +1 -0
  366. package/src/gpu-driven/production-raster.ts +1 -3
  367. package/src/gpu-scene.ts +8 -1
  368. package/src/gpu-usage.ts +2 -0
  369. package/src/hdrp-buffers.ts +165 -59
  370. package/src/ibl/IblPipelineCache.ts +11 -0
  371. package/src/ibl/kernel-cache.ts +88 -0
  372. package/src/index.ts +186 -1
  373. package/src/inspection-types.ts +56 -0
  374. package/src/instances-derived-bounds.ts +171 -0
  375. package/src/light-buffer-layout.ts +74 -26
  376. package/src/mesh-material-bindings.ts +132 -0
  377. package/src/pbr-pipeline.ts +139 -27
  378. package/src/pipeline/standard-forward-lane.ts +65 -15
  379. package/src/pipeline/standard-lighting/errors.ts +6 -0
  380. package/src/pipeline/standard-lighting/graph.ts +157 -0
  381. package/src/pipeline/standard-lighting/inspection.ts +88 -0
  382. package/src/pipeline/standard-lighting/layout.ts +31 -0
  383. package/src/pipeline/standard-lighting/light-frame.ts +34 -0
  384. package/src/pipeline/standard-lighting/membership.ts +43 -0
  385. package/src/pipeline/standard-lighting/prepare.ts +37 -0
  386. package/src/pipeline/standard-lighting/topology.ts +120 -0
  387. package/src/pipeline/standard-lighting/transport.ts +77 -0
  388. package/src/pipeline/standard-pipeline.ts +141 -192
  389. package/src/pipeline/standard-post.ts +26 -7
  390. package/src/pipeline/standard-profile.ts +23 -30
  391. package/src/pipeline-spec.ts +84 -21
  392. package/src/point-shadow-inspection.ts +55 -0
  393. package/src/record/__tests__/temporal-payload.unit.test.ts +42 -1
  394. package/src/record/__tests__/transparent-group2-selection.unit.test.ts +73 -0
  395. package/src/record/__tests__/view-ubo-layout.unit.test.ts +40 -0
  396. package/src/record/frame-lighting.ts +470 -516
  397. package/src/record/frame-snapshot.ts +76 -28
  398. package/src/record/frame-targets.ts +23 -2
  399. package/src/record/frame.ts +1169 -79
  400. package/src/record/gpu-pass-timing/contract.ts +180 -0
  401. package/src/record/gpu-pass-timing/errors.ts +34 -0
  402. package/src/record/gpu-pass-timing/index.ts +37 -0
  403. package/src/record/gpu-pass-timing/parser.ts +97 -0
  404. package/src/record/gpu-pass-timing/session.ts +938 -0
  405. package/src/record/gpu-timing.ts +233 -0
  406. package/src/record/main-pass-geometry.ts +120 -80
  407. package/src/record/main-pass-material.ts +101 -11
  408. package/src/record/main-pass-sprite-draws.ts +54 -25
  409. package/src/record/main-pass.ts +71 -15
  410. package/src/record/render-context.ts +77 -0
  411. package/src/record/shadow-pass.ts +104 -66
  412. package/src/record/skybox-post-pass.ts +20 -4
  413. package/src/record/typed-frame-graph.ts +1016 -32
  414. package/src/record/view-ubo.ts +63 -28
  415. package/src/reflection/filter.ts +81 -0
  416. package/src/reflection/gpu-table.ts +44 -0
  417. package/src/reflection/projection.ts +281 -0
  418. package/src/render-contract.ts +147 -4
  419. package/src/render-data.ts +50 -22
  420. package/src/render-graph-primitives.ts +9 -4
  421. package/src/render-pipeline.ts +161 -2
  422. package/src/render-system-extract.ts +886 -103
  423. package/src/render-system.ts +1174 -66
  424. package/src/renderer-inspect.ts +66 -0
  425. package/src/renderer.ts +222 -0
  426. package/src/scene/render-scene.ts +117 -9
  427. package/src/shadow-atlas.ts +5 -5
  428. package/src/ssao-buffers.ts +9 -2
  429. package/src/targets/contracts.ts +182 -0
  430. package/src/targets/graph-projection.ts +169 -0
  431. package/src/targets/material-source.ts +143 -0
  432. package/src/targets/owner.ts +214 -0
  433. package/src/targets/physical.ts +18 -0
  434. package/src/targets/readback.ts +153 -0
  435. package/src/temporal/frame.ts +128 -0
  436. package/src/transmission/__tests__/standard-transmission.dawn.test.ts +61 -23
  437. package/src/typed-render-graph-primitives.ts +34 -2
  438. package/src/typed-shadow-atlas.ts +17 -0
  439. package/src/typed-shadow-passes.ts +47 -40
  440. package/src/volume/capability.ts +173 -0
  441. package/src/volume/component.ts +110 -0
  442. package/src/volume/extract.ts +21 -0
  443. package/src/volume/inspection.ts +78 -0
  444. package/src/volume/passes.ts +686 -0
  445. package/src/volume/recovery.ts +86 -0
  446. package/src/volume/resources.ts +236 -0
  447. package/src/volume/temporal.ts +89 -0
  448. package/dist/chunk-IPXCABEP.mjs.map +0 -1
  449. package/dist/chunk-LOSO4CTV.mjs.map +0 -1
  450. package/dist/chunk-RJRUS55L.mjs.map +0 -1
  451. package/dist/chunk-YTQ5C7YQ.mjs +0 -584
  452. package/dist/chunk-YTQ5C7YQ.mjs.map +0 -1
@@ -0,0 +1,1294 @@
1
+ import { RenderIntentInvalidError, PointsLinesBudgetExceededError, VolumeOwnerConflictError, VolumeDensityShapeMismatchError, VolumeInvalidBoundsError, VolumeInvalidParametersError, PointsLinesStyleUnsupportedError, PointsLinesMaterialUnsupportedError, PointsLinesInvalidStyleError, PointsLinesTopologyMismatchError } from './chunk-6OPXDQ2E.mjs';
2
+ import { defineComponent, Entity } from '@forgeax/engine-ecs';
3
+ import { TONEMAP_SHADER_MODE } from '@forgeax/engine-shader';
4
+ import { err, ok } from '@forgeax/engine-types';
5
+ import { mat4 } from '@forgeax/engine-math';
6
+ import { Transform, projectHierarchy } from '@forgeax/engine-scene';
7
+ import { createWorldProjection } from '@forgeax/engine-ecs/projection';
8
+
9
+ var Atmosphere = defineComponent("Atmosphere", {
10
+ turbidity: { type: "f32", default: 2 },
11
+ rayleigh: { type: "f32", default: 1 },
12
+ mieCoefficient: { type: "f32", default: 5e-3 },
13
+ mieDirectionalG: { type: "f32", default: 0.8 },
14
+ sunAngularRadius: { type: "f32", default: 4675e-6 }
15
+ });
16
+ var CAMERA_PROJECTION_PERSPECTIVE = 0;
17
+ var CAMERA_PROJECTION_ORTHOGRAPHIC = 1;
18
+ function cameraProjectionFromF32(value) {
19
+ return value === CAMERA_PROJECTION_ORTHOGRAPHIC ? "orthographic" : "perspective";
20
+ }
21
+ var TONEMAP_NONE = TONEMAP_SHADER_MODE.none;
22
+ var TONEMAP_REINHARD_EXTENDED = TONEMAP_SHADER_MODE.reinhardExtended;
23
+ var TONEMAP_REINHARD = TONEMAP_SHADER_MODE.reinhard;
24
+ var TONEMAP_LINEAR = TONEMAP_SHADER_MODE.linear;
25
+ var TONEMAP_CINEON = TONEMAP_SHADER_MODE.cineon;
26
+ var TONEMAP_ACES_FILMIC = TONEMAP_SHADER_MODE.acesFilmic;
27
+ var TONEMAP_AGX = TONEMAP_SHADER_MODE.agx;
28
+ var TONEMAP_NEUTRAL = TONEMAP_SHADER_MODE.neutral;
29
+ function tonemapFromF32(value) {
30
+ switch (value) {
31
+ case TONEMAP_REINHARD_EXTENDED:
32
+ return "reinhard-extended";
33
+ case TONEMAP_REINHARD:
34
+ return "reinhard";
35
+ case TONEMAP_LINEAR:
36
+ return "linear";
37
+ case TONEMAP_CINEON:
38
+ return "cineon";
39
+ case TONEMAP_ACES_FILMIC:
40
+ return "aces-filmic";
41
+ case TONEMAP_AGX:
42
+ return "agx";
43
+ case TONEMAP_NEUTRAL:
44
+ return "neutral";
45
+ default:
46
+ return "none";
47
+ }
48
+ }
49
+ function tonemapToU32(mode) {
50
+ switch (mode) {
51
+ case "reinhard-extended":
52
+ return TONEMAP_REINHARD_EXTENDED;
53
+ case "reinhard":
54
+ return TONEMAP_REINHARD;
55
+ case "linear":
56
+ return TONEMAP_LINEAR;
57
+ case "cineon":
58
+ return TONEMAP_CINEON;
59
+ case "aces-filmic":
60
+ return TONEMAP_ACES_FILMIC;
61
+ case "agx":
62
+ return TONEMAP_AGX;
63
+ case "neutral":
64
+ return TONEMAP_NEUTRAL;
65
+ case "none":
66
+ return TONEMAP_NONE;
67
+ }
68
+ throw new RangeError(`Invalid tonemap mode: ${String(mode)}.`);
69
+ }
70
+ var ANTIALIAS_NONE = 0;
71
+ var ANTIALIAS_FXAA = 1;
72
+ var ANTIALIAS_MSAA = 2;
73
+ var ANTIALIAS_TAA = 3;
74
+ function antialiasFromF32(value) {
75
+ if (value === ANTIALIAS_NONE) return "none";
76
+ if (value === ANTIALIAS_FXAA) return "fxaa";
77
+ if (value === ANTIALIAS_MSAA) return "msaa";
78
+ if (value === ANTIALIAS_TAA) return "taa";
79
+ throw new RangeError(
80
+ `Invalid antialias value: ${value}. Expected ${ANTIALIAS_NONE} (none), ${ANTIALIAS_FXAA} (fxaa), ${ANTIALIAS_MSAA} (msaa), or ${ANTIALIAS_TAA} (taa).`
81
+ );
82
+ }
83
+ var BLOOM_DISABLED = 0;
84
+ var BLOOM_ENABLED = 1;
85
+ function bloomEnabledFromF32(value) {
86
+ if (value === BLOOM_DISABLED) return "off";
87
+ if (value === BLOOM_ENABLED) return "on";
88
+ throw new RangeError(
89
+ `Invalid bloom value: ${value}. Expected ${BLOOM_DISABLED} (off) or ${BLOOM_ENABLED} (on).`
90
+ );
91
+ }
92
+ var Camera = defineComponent("Camera", {
93
+ fov: { type: "f32" },
94
+ aspect: { type: "f32" },
95
+ near: { type: "f32" },
96
+ far: { type: "f32" },
97
+ projection: { type: "f32", default: 0 },
98
+ left: { type: "f32", default: -1 },
99
+ right: { type: "f32", default: 1 },
100
+ bottom: { type: "f32", default: -1 },
101
+ top: { type: "f32", default: 1 },
102
+ tonemap: { type: "f32", default: 0 },
103
+ exposure: { type: "f32", default: 1 },
104
+ whitePoint: { type: "f32", default: 4 },
105
+ antialias: { type: "f32", default: 0 },
106
+ historyVersion: { type: "u32", default: 0 },
107
+ bloom: { type: "f32", default: 0 },
108
+ bloomThreshold: { type: "f32", default: 1 },
109
+ bloomIntensity: { type: "f32", default: 1 },
110
+ bloomBlurRadius: { type: "f32", default: 4 },
111
+ // M1 target camera role: zero means this camera is eligible for display;
112
+ // non-zero shared targets are auxiliary producers whose views and receipt
113
+ // promotion remain owned by Renderer.
114
+ target: { type: "shared<RenderTarget>", simulationTransient: true },
115
+ // feat-20260709 M3 / D-3: clear-color is one inline `array<f32,4>` column.
116
+ // The earlier 4-scalar form (clearR/G/B/A) was chosen when this was believed
117
+ // to be the only SoA-safe shape; the Transform (pos/quat/scale) and light
118
+ // (direction/color) precedents disprove that -- an `array<f32,N>` IS an
119
+ // inline stride-N SoA column, read on the hot path as `col[i*N+a]` with zero
120
+ // allocation, so collapsing four scalars into one column removes three field
121
+ // names a reader must track without changing the storage layout or read
122
+ // pattern. The default `[0, 0, 0, 0]` is transparent black; explicit alpha
123
+ // stays visible through the same public array field.
124
+ clearColor: { type: "array<f32, 4>", default: new Float32Array([0, 0, 0, 0]) },
125
+ // feat-20260617-host-engine-contract-and-video-cutscene / M3 / D-4: the
126
+ // aspect-sync sidecar on the createApp(canvas) path writes
127
+ // canvas.width / canvas.height into `aspect` every frame when this flag is
128
+ // true. Reuses the existing `bool` column tier (AnimationPlayer.paused /
129
+ // AudioSource.playing precedent) -- zero ECS infrastructure change. Default
130
+ // true so demos that never touch aspect track the canvas automatically
131
+ // (charter P1 default-is-correct); set false for render-to-texture /
132
+ // split-screen cameras that drive aspect themselves. Read it via
133
+ // world.get (readRow narrows bool -> JS boolean); the query-bundle path
134
+ // returns a raw 0/1 number (the `!== 0` always-true trap).
135
+ autoAspect: { type: "bool", default: true }
136
+ });
137
+ function cameraPodFromDefaults() {
138
+ const base = {};
139
+ for (const [key, field] of Object.entries(Camera.fields)) {
140
+ if ("default" in field && field.default !== void 0) {
141
+ base[key] = field.default;
142
+ }
143
+ }
144
+ return base;
145
+ }
146
+ function perspective(opts) {
147
+ return {
148
+ // cameraPodFromDefaults() reads autoAspect's schema default (true), so an
149
+ // omitted opts.autoAspect lands the default-correct value; an explicit
150
+ // false overrides it below (D-4: factory one-step opt-out, charter P1).
151
+ ...cameraPodFromDefaults(),
152
+ fov: opts.fov,
153
+ aspect: opts.aspect,
154
+ near: opts.near ?? 0.1,
155
+ far: opts.far ?? 100,
156
+ projection: CAMERA_PROJECTION_PERSPECTIVE,
157
+ ...opts.autoAspect !== void 0 ? { autoAspect: opts.autoAspect } : {}
158
+ };
159
+ }
160
+ function orthographic(opts) {
161
+ return {
162
+ ...cameraPodFromDefaults(),
163
+ fov: 0,
164
+ aspect: 1,
165
+ near: opts.near ?? 0.1,
166
+ far: opts.far ?? 100,
167
+ projection: CAMERA_PROJECTION_ORTHOGRAPHIC,
168
+ left: opts.left,
169
+ right: opts.right,
170
+ bottom: opts.bottom,
171
+ top: opts.top
172
+ };
173
+ }
174
+ var CUBE_CAMERA_UPDATE_ONCE = 0;
175
+ var CUBE_CAMERA_UPDATE_ON_DEMAND = 1;
176
+ var CUBE_CAMERA_UPDATE_CONTINUOUS = 2;
177
+ var CUBE_CAMERA_FACE_ORDER = ["+X", "-X", "+Y", "-Y", "+Z", "-Z"];
178
+ function cubeCameraUpdateIntentFromF32(value) {
179
+ switch (value) {
180
+ case CUBE_CAMERA_UPDATE_ONCE:
181
+ return "once";
182
+ case CUBE_CAMERA_UPDATE_ON_DEMAND:
183
+ return "on-demand";
184
+ case CUBE_CAMERA_UPDATE_CONTINUOUS:
185
+ return "continuous";
186
+ default:
187
+ throw new RenderIntentInvalidError("CubeCamera", value);
188
+ }
189
+ }
190
+ function cubeCameraUpdateIntentToF32(intent) {
191
+ switch (intent) {
192
+ case "once":
193
+ return CUBE_CAMERA_UPDATE_ONCE;
194
+ case "on-demand":
195
+ return CUBE_CAMERA_UPDATE_ON_DEMAND;
196
+ case "continuous":
197
+ return CUBE_CAMERA_UPDATE_CONTINUOUS;
198
+ }
199
+ }
200
+ var CubeCamera = defineComponent("CubeCamera", {
201
+ target: { type: "shared<RenderTarget>", simulationTransient: true },
202
+ near: { type: "f32", default: 0.1 },
203
+ far: { type: "f32", default: 100 },
204
+ updateIntent: { type: "f32", default: CUBE_CAMERA_UPDATE_ONCE },
205
+ requestVersion: { type: "u32", default: 0 },
206
+ faceBudget: { type: "u32", default: 1 }
207
+ });
208
+
209
+ // src/components/directional-shadow-filter.ts
210
+ var DirectionalShadowFilterValue = Object.freeze({
211
+ pcf1: 1,
212
+ pcf3: 2,
213
+ pcf5: 3,
214
+ pcssMedium: 4,
215
+ pcssHigh: 5
216
+ });
217
+ function directionalShadowQualityFromF32(value, angularRadiusRadians, maxPenumbraTexels) {
218
+ switch (value) {
219
+ case DirectionalShadowFilterValue.pcf1:
220
+ return { kind: "pcf", kernel: 1 };
221
+ case DirectionalShadowFilterValue.pcf3:
222
+ return { kind: "pcf", kernel: 3 };
223
+ case DirectionalShadowFilterValue.pcf5:
224
+ return { kind: "pcf", kernel: 5 };
225
+ case DirectionalShadowFilterValue.pcssMedium:
226
+ return {
227
+ kind: "pcss",
228
+ preset: "medium",
229
+ angularRadiusRadians,
230
+ maxPenumbraTexels
231
+ };
232
+ case DirectionalShadowFilterValue.pcssHigh:
233
+ return {
234
+ kind: "pcss",
235
+ preset: "high",
236
+ angularRadiusRadians,
237
+ maxPenumbraTexels
238
+ };
239
+ default:
240
+ return void 0;
241
+ }
242
+ }
243
+ var DirectionalLight = defineComponent("DirectionalLight", {
244
+ // direction is the ONLY field with no default (D-5): omitting it lands the
245
+ // array layer-3 all-zero [0,0,0], which the renderer owner rejects -- there is no
246
+ // universal default direction, so "default is illegal" forces an explicit
247
+ // non-zero value. color carries an explicit layer-2 default [1,1,1] (white);
248
+ // the array layer-3 fallback is all-zero, so the default MUST be explicit.
249
+ direction: { type: "array<f32, 3>" },
250
+ color: { type: "array<f32, 3>", default: new Float32Array([1, 1, 1]) },
251
+ intensity: { type: "f32", default: 1 },
252
+ // Shadow opt-out gate: defaults to true so zero-config spawns get shadows.
253
+ castShadow: { type: "bool", default: true },
254
+ // 9 shadow fields migrated from DirectionalLightShadow (feat-20260621 M1).
255
+ cascadeCount: { type: "f32", default: 4 },
256
+ splitLambda: { type: "f32", default: 0.75 },
257
+ cascadeBlend: { type: "f32", default: 0.2 },
258
+ mapSize: { type: "f32", default: 2048 },
259
+ depthBias: { type: "f32", default: 5e-3 },
260
+ normalBias: { type: "f32", default: 0.05 },
261
+ // Sole shadow coverage knob (feat replaces nearPlane/farPlane). The PSSM
262
+ // near end derives from the active camera near; shadowDistance is the far
263
+ // reach. Default 200 world units (matches UE-style "dynamic shadow
264
+ // distance"; the old farPlane default was 50).
265
+ shadowDistance: { type: "f32", default: 200 },
266
+ shadowFilter: {
267
+ type: "enum",
268
+ default: DirectionalShadowFilterValue.pcf3,
269
+ labels: DirectionalShadowFilterValue
270
+ },
271
+ shadowAngularRadius: { type: "f32", default: 465e-5 },
272
+ maxPenumbraTexels: { type: "f32", default: 32 }
273
+ });
274
+ var Fog = defineComponent("Fog", {
275
+ color: { type: "array<f32, 3>", default: new Float32Array([0.5, 0.5, 0.5]) },
276
+ density: { type: "f32", default: 0.01 },
277
+ heightFalloff: { type: "f32", default: 0 },
278
+ maxOpacity: { type: "f32", default: 1 }
279
+ });
280
+ var Instances = defineComponent("Instances", {
281
+ transforms: { type: "array<f32>" }
282
+ });
283
+ var Layer = defineComponent("Layer", {
284
+ value: { type: "i32", default: 0 }
285
+ });
286
+ var Lines = defineComponent(
287
+ "Lines",
288
+ {
289
+ widthPx: { type: "f32", default: 1 }
290
+ },
291
+ {
292
+ meta: {
293
+ quickStart: "Attach Lines to render line-list MeshAsset pairs in screen pixels.",
294
+ diagnostics: "Inspect widthPx at the points-lines admission boundary.",
295
+ recovery: "Use points-lines-invalid-style for non-finite or non-positive widthPx values.",
296
+ boundaries: "Lines owns raster width only; MeshFilter and MeshRenderer own geometry and material."
297
+ }
298
+ }
299
+ );
300
+ var MeshFilter = defineComponent("MeshFilter", {
301
+ assetHandle: { type: "shared<MeshAsset>" }
302
+ });
303
+ var MeshRenderer = defineComponent("MeshRenderer", {
304
+ materials: { type: "array<shared<MaterialAsset>>", default: [] }
305
+ });
306
+ var MotionBlur = defineComponent("MotionBlur", {
307
+ shutterAngle: { type: "f32", default: 180 },
308
+ maxRadiusPixels: { type: "f32", default: 32 },
309
+ sampleCount: { type: "f32", default: 8 }
310
+ });
311
+ var PointLight = defineComponent("PointLight", {
312
+ // color carries an explicit layer-2 default [1,1,1] (white); the array
313
+ // layer-3 fallback is all-zero, so the default MUST be explicit (D-5).
314
+ color: { type: "array<f32, 3>", default: new Float32Array([1, 1, 1]) },
315
+ intensity: { type: "f32", default: 1 },
316
+ range: { type: "f32", default: 10 }
317
+ });
318
+ var PointLightShadow = defineComponent("PointLightShadow", {
319
+ mapSize: { type: "f32", default: 512 },
320
+ depthBias: { type: "f32", default: 5e-3 },
321
+ normalBias: { type: "f32", default: 0.05 },
322
+ nearPlane: { type: "f32", default: 0.1 },
323
+ farPlane: { type: "f32", default: 25 },
324
+ pcfKernelSize: { type: "f32", default: 3 }
325
+ });
326
+ var PointShapeValue = Object.freeze({
327
+ square: 0,
328
+ circle: 1
329
+ });
330
+ function pointShapeFromU32(value) {
331
+ switch (value) {
332
+ case PointShapeValue.square:
333
+ return "square";
334
+ case PointShapeValue.circle:
335
+ return "circle";
336
+ default:
337
+ return void 0;
338
+ }
339
+ }
340
+ var Points = defineComponent(
341
+ "Points",
342
+ {
343
+ sizePx: { type: "f32", default: 4 },
344
+ shape: { type: "enum", default: PointShapeValue.square, labels: PointShapeValue }
345
+ },
346
+ {
347
+ meta: {
348
+ quickStart: "Attach Points to render point-list MeshAsset vertices in screen pixels.",
349
+ diagnostics: "Inspect sizePx and decode shape with pointShapeFromU32.",
350
+ recovery: "Use points-lines-invalid-style for non-finite or non-positive sizePx values.",
351
+ boundaries: "Points owns raster size and shape only; MeshFilter and MeshRenderer own geometry and material."
352
+ }
353
+ }
354
+ );
355
+ var schema = {
356
+ shader: "string",
357
+ data: "buffer"
358
+ };
359
+ var PostProcessParams = defineComponent("PostProcessParams", schema);
360
+ var REFLECTION_PROBE_UPDATE_ONCE = 0;
361
+ var REFLECTION_PROBE_UPDATE_ON_CHANGE = 1;
362
+ var REFLECTION_PROBE_UPDATE_CONTINUOUS = 2;
363
+ function reflectionProbeUpdateIntentFromF32(value) {
364
+ switch (value) {
365
+ case REFLECTION_PROBE_UPDATE_ONCE:
366
+ return "once";
367
+ case REFLECTION_PROBE_UPDATE_ON_CHANGE:
368
+ return "on-change";
369
+ case REFLECTION_PROBE_UPDATE_CONTINUOUS:
370
+ return "continuous";
371
+ default:
372
+ throw new RenderIntentInvalidError("ReflectionProbe", value);
373
+ }
374
+ }
375
+ function reflectionProbeUpdateIntentToF32(intent) {
376
+ switch (intent) {
377
+ case "once":
378
+ return REFLECTION_PROBE_UPDATE_ONCE;
379
+ case "on-change":
380
+ return REFLECTION_PROBE_UPDATE_ON_CHANGE;
381
+ case "continuous":
382
+ return REFLECTION_PROBE_UPDATE_CONTINUOUS;
383
+ }
384
+ }
385
+ var ReflectionProbe = defineComponent("ReflectionProbe", {
386
+ halfExtents: { type: "array<f32, 3>", default: new Float32Array([1, 1, 1]) },
387
+ priority: { type: "f32", default: 0 },
388
+ intensity: { type: "f32", default: 1 },
389
+ resolution: { type: "u32", default: 256 },
390
+ updateIntent: { type: "f32", default: REFLECTION_PROBE_UPDATE_ONCE },
391
+ invalidationVersion: { type: "u32", default: 0 }
392
+ });
393
+ var SceneInstance = defineComponent(
394
+ "SceneInstance",
395
+ {
396
+ source: { type: "shared<SceneAsset>" },
397
+ mapping: { type: "array<entity>" },
398
+ // The unique slot is the ECS storage seam for the instance's structured
399
+ // runtime payload; keep the nested semantic visible to schema consumers.
400
+ state: { type: "unique<SceneInstanceState>", shape: "nested" }
401
+ },
402
+ { transient: true }
403
+ );
404
+ var SKYBOX_MODE_CUBEMAP = 0;
405
+ var SkyboxBackground = defineComponent("SkyboxBackground", {
406
+ // The GPU skybox projection is render-owned presentation state; restore
407
+ // keeps the portable mode/rotation controls and lets the owner re-resolve
408
+ // the environment asset on the target world.
409
+ equirect: { type: "shared<EquirectAsset>", simulationTransient: true },
410
+ mode: { type: "f32", default: SKYBOX_MODE_CUBEMAP },
411
+ rotation: { type: "array<f32, 4>", default: new Float32Array([0, 0, 0, 1]) }
412
+ });
413
+ var Skylight = defineComponent("Skylight", {
414
+ // The GPU equirect-to-cubemap projection is render-owned presentation
415
+ // state. Record/restore preserves the portable lighting controls while the
416
+ // render owner resolves this asset again on the target world.
417
+ equirect: { type: "shared<EquirectAsset>", simulationTransient: true },
418
+ // color carries an explicit layer-2 default [1,1,1] (white); the array
419
+ // layer-3 fallback is all-zero, so the default MUST be explicit (D-5).
420
+ color: { type: "array<f32, 3>", default: new Float32Array([1, 1, 1]) },
421
+ intensity: { type: "f32", default: 1 },
422
+ rotation: { type: "array<f32, 4>", default: new Float32Array([0, 0, 0, 1]) }
423
+ });
424
+ var SortKey = defineComponent("SortKey", {
425
+ value: { type: "f32", default: 0 }
426
+ });
427
+ var SpotLight = defineComponent("SpotLight", {
428
+ // direction has no default (D-5): omitting it lands the array layer-3
429
+ // all-zero, which validate() rejects. color carries an explicit layer-2
430
+ // default [1,1,1] (white); the array layer-3 fallback is all-zero.
431
+ direction: { type: "array<f32, 3>" },
432
+ color: { type: "array<f32, 3>", default: new Float32Array([1, 1, 1]) },
433
+ intensity: { type: "f32", default: 1 },
434
+ range: { type: "f32", default: 10 },
435
+ innerConeDeg: { type: "f32", default: 0 },
436
+ outerConeDeg: { type: "f32", default: 45 },
437
+ // Shadow opt-out gate: defaults to true so zero-config spawns cast shadows.
438
+ castShadow: { type: "bool", default: true },
439
+ // 7 shadow fields aligned with the shared light shadow vocabulary.
440
+ mapSize: { type: "f32", default: 2048 },
441
+ depthBias: { type: "f32", default: 5e-3 },
442
+ normalBias: { type: "f32", default: 0.05 },
443
+ nearPlane: { type: "f32", default: 0.1 },
444
+ farPlane: { type: "f32", default: 50 },
445
+ pcfKernelSize: { type: "f32", default: 3 },
446
+ // Three's SpotLight.shadow.intensity is the fraction of visibility applied
447
+ // to a shadowed contribution. Keep it in the author-facing [0,1] domain;
448
+ // extract/record transport this one fact to surface and volume consumers.
449
+ shadowIntensity: { type: "f32", default: 1 },
450
+ // Shared TextureAsset identity; publication and residency remain owned by
451
+ // the existing asset and GPU resource owners.
452
+ projector: { type: "shared<TextureAsset>", simulationTransient: true }
453
+ });
454
+ var VisibilityStateValue = Object.freeze({
455
+ inherited: 0,
456
+ hidden: 1,
457
+ visible: 2
458
+ });
459
+ function visibilityStateFromU32(value) {
460
+ switch (value) {
461
+ case VisibilityStateValue.inherited:
462
+ return "inherited";
463
+ case VisibilityStateValue.hidden:
464
+ return "hidden";
465
+ case VisibilityStateValue.visible:
466
+ return "visible";
467
+ default:
468
+ return void 0;
469
+ }
470
+ }
471
+ var Visibility = defineComponent(
472
+ "Visibility",
473
+ {
474
+ state: {
475
+ type: "enum",
476
+ default: VisibilityStateValue.inherited,
477
+ labels: VisibilityStateValue
478
+ }
479
+ },
480
+ {
481
+ meta: {
482
+ quickStart: "Attach Visibility to author render participation intent.",
483
+ diagnostics: "Compare the ECS state with resolveVisibility and renderer.visibilityStats.",
484
+ recovery: "Use structured ECS write errors and repair the owning scene relation.",
485
+ boundaries: "Visibility does not own camera, picking, lifecycle, assets, or VFX shadow policy."
486
+ }
487
+ }
488
+ );
489
+ function invalidStyle(input, component, field, value, expected) {
490
+ return err(new PointsLinesInvalidStyleError({ entity: input.entity, component, field, value, expected }));
491
+ }
492
+ function topologyMismatch(input, submesh, expected, actual) {
493
+ return err(new PointsLinesTopologyMismatchError({ entity: input.entity, submesh, expected, actual }));
494
+ }
495
+ function checkStyle(input) {
496
+ const hasPoints = input.points !== void 0;
497
+ const hasLines = input.lines !== void 0;
498
+ if (hasPoints && hasLines) {
499
+ return invalidStyle(
500
+ input,
501
+ "Points/Lines",
502
+ "components",
503
+ "Points + Lines",
504
+ "exactly one style component"
505
+ );
506
+ }
507
+ if (!hasPoints && !hasLines) {
508
+ return invalidStyle(input, "Points/Lines", "components", void 0, "one style component");
509
+ }
510
+ if (hasPoints) {
511
+ const sizePx = input.points?.sizePx ?? 4;
512
+ const shapeValue = input.points?.shape ?? PointShapeValue.square;
513
+ const shape = pointShapeFromU32(shapeValue);
514
+ if (!Number.isFinite(sizePx) || sizePx <= 0) {
515
+ return invalidStyle(input, "Points", "sizePx", sizePx, "finite sizePx > 0");
516
+ }
517
+ if (shape === void 0) {
518
+ return invalidStyle(input, "Points", "shape", shapeValue, "shape is 'square' or 'circle'");
519
+ }
520
+ return ok({ component: "Points", shape, sizePx });
521
+ }
522
+ const widthPx = input.lines?.widthPx ?? 1;
523
+ if (!Number.isFinite(widthPx) || widthPx <= 0) {
524
+ return invalidStyle(input, "Lines", "widthPx", widthPx, "finite widthPx > 0");
525
+ }
526
+ return ok({ component: "Lines", widthPx });
527
+ }
528
+ function checkTopology(input, component) {
529
+ const expected = component === "Points" ? "point-list" : "line-list";
530
+ let pointCount = 0;
531
+ let segmentCount = 0;
532
+ const acceptedSubmeshes = [];
533
+ let sawNonEmpty = false;
534
+ for (const [submeshIndex, submesh] of input.mesh.submeshes.entries()) {
535
+ const indexed = input.mesh.indices !== void 0 && submesh.indexCount > 0;
536
+ const elementCount = indexed ? submesh.indexCount : submesh.vertexCount;
537
+ if (elementCount === 0) continue;
538
+ sawNonEmpty = true;
539
+ if (component === "Lines" && submesh.topology === "line-strip") {
540
+ return err(
541
+ new PointsLinesStyleUnsupportedError({
542
+ lane: "admission",
543
+ field: "topology",
544
+ member: "line-strip",
545
+ supported: ["line-list"]
546
+ })
547
+ );
548
+ }
549
+ if (submesh.topology !== expected) {
550
+ return topologyMismatch(input, submeshIndex, expected, submesh.topology);
551
+ }
552
+ if (component === "Lines" && elementCount % 2 !== 0) {
553
+ return topologyMismatch(input, submeshIndex, "line-list pairs", "line-list odd tail");
554
+ }
555
+ acceptedSubmeshes.push(submeshIndex);
556
+ if (component === "Points") pointCount += elementCount;
557
+ else segmentCount += elementCount / 2;
558
+ }
559
+ if (!sawNonEmpty) return topologyMismatch(input, 0, expected, "empty-range");
560
+ return ok({ pointCount, segmentCount, submeshes: acceptedSubmeshes });
561
+ }
562
+ function checkMaterial(input) {
563
+ const passes = input.material.passes ?? [];
564
+ const unlitModules = /* @__PURE__ */ new Set(["forgeax_material::unlit", "forgeax::default-unlit"]);
565
+ if (passes.length !== 1 || passes[0] === void 0) {
566
+ const pass2 = passes.find(
567
+ (candidate) => candidate.name === "deferred" || candidate.name === "shadow-caster"
568
+ ) ?? passes[0];
569
+ return err(
570
+ new PointsLinesMaterialUnsupportedError({
571
+ entity: input.entity,
572
+ material: input.materialId ?? "<anonymous>",
573
+ pass: pass2?.name ?? "<empty>",
574
+ module: pass2?.program.module ?? "<empty>",
575
+ reason: "points-lines admission requires one unlit forward pass"
576
+ })
577
+ );
578
+ }
579
+ const pass = passes[0];
580
+ const lightMode = pass.renderState?.tags?.LightMode;
581
+ if (!unlitModules.has(pass.program.module) || pass.name !== "forward" || lightMode === "ShadowCaster") {
582
+ return err(
583
+ new PointsLinesMaterialUnsupportedError({
584
+ entity: input.entity,
585
+ material: input.materialId ?? "<anonymous>",
586
+ pass: pass.name,
587
+ module: pass.program.module,
588
+ reason: "points-lines admission requires an engine-owned unlit forward pass"
589
+ })
590
+ );
591
+ }
592
+ return ok(void 0);
593
+ }
594
+ function admitPointsLines(input) {
595
+ const style = checkStyle(input);
596
+ if (!style.ok) return style;
597
+ const topology = checkTopology(input, style.value.component);
598
+ if (!topology.ok) return topology;
599
+ const material = checkMaterial(input);
600
+ if (!material.ok) return material;
601
+ if (style.value.component === "Points" && input.limits?.maxPoints !== void 0) {
602
+ if (topology.value.pointCount > input.limits.maxPoints) {
603
+ return err(
604
+ new PointsLinesBudgetExceededError({
605
+ lane: "admission",
606
+ requested: topology.value.pointCount,
607
+ limit: input.limits.maxPoints,
608
+ unit: "points"
609
+ })
610
+ );
611
+ }
612
+ }
613
+ if (style.value.component === "Lines" && input.limits?.maxSegments !== void 0) {
614
+ if (topology.value.segmentCount > input.limits.maxSegments) {
615
+ return err(
616
+ new PointsLinesBudgetExceededError({
617
+ lane: "admission",
618
+ requested: topology.value.segmentCount,
619
+ limit: input.limits.maxSegments,
620
+ unit: "segments"
621
+ })
622
+ );
623
+ }
624
+ }
625
+ return ok({ ...style.value, ...topology.value });
626
+ }
627
+
628
+ // src/volume/inspection.ts
629
+ function resourceStage(input) {
630
+ if (!input.authored || input.recovery === void 0) return "none";
631
+ if (input.recovery.status === "candidate") return "candidate";
632
+ if (input.recovery.status === "recovering") return "recovering";
633
+ if (input.recovery.status === "degraded") return "lkg";
634
+ return "accepted";
635
+ }
636
+ function inspectVolumetricFog(input) {
637
+ const recovery = input.recovery;
638
+ const status = !input.authored ? "off" : input.capability === "unavailable" ? "unavailable" : input.degraded === true || recovery?.status === "degraded" ? "degraded" : "available";
639
+ return Object.freeze({
640
+ status,
641
+ resourceStage: resourceStage(input),
642
+ guid: recovery?.guid,
643
+ generation: recovery?.generation,
644
+ digest: input.acceptedDigest,
645
+ candidateGeneration: recovery?.candidateGeneration,
646
+ candidateDigest: recovery?.candidateDigest,
647
+ lkgGeneration: recovery?.lkgGeneration,
648
+ candidateFailure: recovery?.candidateFailure,
649
+ deviceEpoch: recovery?.deviceEpoch ?? 0,
650
+ format: input.format,
651
+ passCount: input.passCount ?? 0,
652
+ sampleCount: input.sampleCount ?? 0,
653
+ memoryBytes: input.memoryBytes ?? 0,
654
+ resourceFacts: input.resourceFacts,
655
+ selectedLight: recovery?.selectedLight,
656
+ candidateSelectedLight: recovery?.candidateSelectedLight
657
+ });
658
+ }
659
+ var FACE_ORIENTATION = [
660
+ { direction: [1, 0, 0], up: [0, 1, 0] },
661
+ { direction: [-1, 0, 0], up: [0, 1, 0] },
662
+ { direction: [0, 1, 0], up: [0, 0, -1] },
663
+ { direction: [0, -1, 0], up: [0, 0, 1] },
664
+ { direction: [0, 0, 1], up: [0, 1, 0] },
665
+ { direction: [0, 0, -1], up: [0, 1, 0] }
666
+ ];
667
+ function assertRange(near, far) {
668
+ if (!Number.isFinite(near) || near <= 0 || !Number.isFinite(far) || far <= near) {
669
+ throw new RangeError("CubeCamera requires finite near > 0 and far > near.");
670
+ }
671
+ }
672
+ function buildCubeCameraFaceViews(input) {
673
+ assertRange(input.near, input.far);
674
+ const views = [];
675
+ for (const [index, face] of CUBE_CAMERA_FACE_ORDER.entries()) {
676
+ const orientation = FACE_ORIENTATION[index];
677
+ if (orientation === void 0)
678
+ throw new Error(`CubeCamera face orientation missing at ${index}`);
679
+ const target = [
680
+ (input.position[0] ?? 0) + orientation.direction[0],
681
+ (input.position[1] ?? 0) + orientation.direction[1],
682
+ (input.position[2] ?? 0) + orientation.direction[2]
683
+ ];
684
+ const view = mat4.lookAt(mat4.create(), input.position, target, orientation.up);
685
+ const projection = mat4.perspective(mat4.create(), Math.PI / 2, 1, input.near, input.far);
686
+ const viewProjection = mat4.multiply(mat4.create(), projection, view);
687
+ views.push({
688
+ position: input.position,
689
+ face,
690
+ direction: orientation.direction,
691
+ up: orientation.up,
692
+ near: input.near,
693
+ far: input.far,
694
+ view,
695
+ projection,
696
+ viewProjection
697
+ });
698
+ }
699
+ return views;
700
+ }
701
+
702
+ // src/mesh-material-bindings.ts
703
+ var EMPTY_RESIDENCY = Object.freeze({
704
+ readiness: "pending",
705
+ samplers: Object.freeze([]),
706
+ textures: Object.freeze([])
707
+ });
708
+ function projectMeshMaterialBindingObservation(input) {
709
+ const residency = input.bindings.map((_, index) => {
710
+ const value = input.residency?.[index] ?? EMPTY_RESIDENCY;
711
+ return Object.freeze({
712
+ ...value,
713
+ samplers: Object.freeze(value.samplers.map((sampler) => Object.freeze({ ...sampler }))),
714
+ textures: Object.freeze(value.textures.map((texture) => Object.freeze({ ...texture }))),
715
+ ...value.preparationFailure === void 0 ? {} : {
716
+ preparationFailure: Object.freeze({
717
+ ...value.preparationFailure,
718
+ ...value.preparationFailure.detail === void 0 ? {} : { detail: Object.freeze({ ...value.preparationFailure.detail }) }
719
+ })
720
+ }
721
+ });
722
+ });
723
+ return {
724
+ worldId: input.worldId,
725
+ entityKey: input.entityKey,
726
+ bindings: Object.freeze([...input.bindings]),
727
+ diagnostics: Object.freeze([...input.diagnostics]),
728
+ residency: Object.freeze(residency)
729
+ };
730
+ }
731
+ function summarizeMeshMaterialBindings(observations) {
732
+ let ready = 0;
733
+ let pending = 0;
734
+ let failed = 0;
735
+ let lastKnownGood = 0;
736
+ let textureCount = 0;
737
+ let samplerCount = 0;
738
+ for (const observation of observations) {
739
+ for (const residency of observation.residency) {
740
+ switch (residency.readiness) {
741
+ case "ready":
742
+ ready += 1;
743
+ break;
744
+ case "pending":
745
+ pending += 1;
746
+ break;
747
+ case "failed":
748
+ failed += 1;
749
+ break;
750
+ case "last-known-good":
751
+ lastKnownGood += 1;
752
+ break;
753
+ }
754
+ textureCount += residency.textures.length;
755
+ samplerCount += residency.samplers.length;
756
+ }
757
+ }
758
+ return {
759
+ total: ready + pending + failed + lastKnownGood,
760
+ ready,
761
+ pending,
762
+ failed,
763
+ lastKnownGood,
764
+ textureCount,
765
+ samplerCount
766
+ };
767
+ }
768
+ function resolveMeshMaterialBindings(mesh, rendererOverrides, deps) {
769
+ const bindings = [];
770
+ const diagnostics = [];
771
+ if (!Array.isArray(mesh.materialSlots)) {
772
+ return { ok: false, code: "mesh-material-slots-missing" };
773
+ }
774
+ if (rendererOverrides.length > mesh.materialSlots.length) {
775
+ diagnostics.push({
776
+ code: "mesh-renderer-material-override-overflow",
777
+ slotIndex: mesh.materialSlots.length
778
+ });
779
+ }
780
+ for (let slotIndex = 0; slotIndex < mesh.materialSlots.length; slotIndex++) {
781
+ const override = rendererOverrides[slotIndex] ?? 0;
782
+ if (override !== 0 && deps.isValidOverride(override)) {
783
+ bindings.push({ handle: override, source: "renderer-override" });
784
+ continue;
785
+ }
786
+ if (override !== 0) {
787
+ diagnostics.push({
788
+ code: "mesh-renderer-material-override-invalid",
789
+ slotIndex,
790
+ handle: override
791
+ });
792
+ }
793
+ const declaredDefault = mesh.materialSlots[slotIndex]?.defaultMaterial;
794
+ if (declaredDefault !== void 0) {
795
+ const handle = deps.resolveMeshDefault(declaredDefault);
796
+ if (handle === void 0) {
797
+ return {
798
+ ok: false,
799
+ code: "mesh-default-material-not-ready",
800
+ slotIndex,
801
+ defaultMaterial: declaredDefault
802
+ };
803
+ }
804
+ bindings.push({ handle, source: "mesh-default" });
805
+ continue;
806
+ }
807
+ bindings.push({ handle: 0, source: "engine-default" });
808
+ }
809
+ return { ok: true, bindings, diagnostics };
810
+ }
811
+
812
+ // src/point-shadow-inspection.ts
813
+ function inspectPointShadow(snapshots, shadowAtlasCapacity) {
814
+ const requested = snapshots.length;
815
+ const admitted = snapshots.reduce(
816
+ (count, snapshot) => snapshot.shadowAtlasLayer >= 0 && snapshot.shadowAtlasLayer < shadowAtlasCapacity ? count + 1 : count,
817
+ 0
818
+ );
819
+ const status = requested === 0 ? "inactive" : requested > shadowAtlasCapacity || admitted !== requested ? "over-budget" : "ready";
820
+ return Object.freeze({
821
+ status,
822
+ requested,
823
+ admitted,
824
+ // A point shadow only samples an atlas after it owns an atlas layer. Keep
825
+ // these names explicit for AI diagnostics while deriving both from the
826
+ // one layer projection above.
827
+ shadowed: admitted,
828
+ shadowAtlasOccupancy: admitted,
829
+ shadowAtlasCapacity
830
+ });
831
+ }
832
+ function hasVolumetricFogCapability(caps, shaders) {
833
+ return caps.compute && caps.storageTexture && shaders !== void 0;
834
+ }
835
+ function resolveVolumetricFogLightPair(world, point, spot) {
836
+ const expected = "PointLight + SpotLight in the same World";
837
+ if (!world.get(point, Entity).ok || !world.get(spot, Entity).ok) {
838
+ return {
839
+ status: "unresolved",
840
+ reason: "missing-entity",
841
+ point,
842
+ spot,
843
+ expected,
844
+ actual: "point or spot entity is absent from this World",
845
+ hint: "assign two live entities from the same World and retry"
846
+ };
847
+ }
848
+ if (point === spot) {
849
+ return {
850
+ status: "unresolved",
851
+ reason: "same-entity",
852
+ point,
853
+ spot,
854
+ expected,
855
+ actual: "PointLight and SpotLight share one EntityHandle",
856
+ hint: "spawn PointLight and SpotLight on two different entities"
857
+ };
858
+ }
859
+ const pointValid = world.hasComponent(point, PointLight) && world.hasComponent(point, Transform);
860
+ const spotValid = world.hasComponent(spot, SpotLight) && world.hasComponent(spot, Transform);
861
+ if (!pointValid || !spotValid) {
862
+ return {
863
+ status: "unresolved",
864
+ reason: "wrong-component",
865
+ point,
866
+ spot,
867
+ expected,
868
+ actual: `pointValid=${pointValid}; spotValid=${spotValid}`,
869
+ hint: "attach PointLight and SpotLight with Transform to separate same-World entities"
870
+ };
871
+ }
872
+ return { status: "available", mode: "point-spot", point, spot };
873
+ }
874
+ function resolveSelectedVolumetricLight(world, entity) {
875
+ const expected = "DirectionalLight | PointLight | SpotLight";
876
+ if (!world.get(entity, Entity).ok) {
877
+ return {
878
+ status: "unresolved",
879
+ reason: "missing-entity",
880
+ entity,
881
+ expected,
882
+ actual: "missing or foreign World entity",
883
+ hint: "assign VolumetricFog.light to a live DirectionalLight or SpotLight in this World"
884
+ };
885
+ }
886
+ const hasDirectional = world.hasComponent(entity, DirectionalLight);
887
+ const hasSpot = world.hasComponent(entity, SpotLight);
888
+ if (hasDirectional && hasSpot) {
889
+ return {
890
+ status: "unresolved",
891
+ reason: "ambiguous",
892
+ entity,
893
+ expected,
894
+ actual: "DirectionalLight + SpotLight",
895
+ hint: "remove one mutually exclusive light component from the selected entity"
896
+ };
897
+ }
898
+ if (hasDirectional) return { status: "available", entity, kind: "directional" };
899
+ if (world.hasComponent(entity, PointLight) && world.hasComponent(entity, Transform)) {
900
+ return { status: "available", entity, kind: "point" };
901
+ }
902
+ if (hasSpot && world.hasComponent(entity, Transform)) {
903
+ return { status: "available", entity, kind: "spot" };
904
+ }
905
+ const actual = hasSpot ? "SpotLight without Transform" : world.hasComponent(entity, PointLight) ? "PointLight" : "other component";
906
+ return {
907
+ status: "unresolved",
908
+ reason: "wrong-component",
909
+ entity,
910
+ expected,
911
+ actual,
912
+ hint: "add exactly one supported light component and a Transform for PointLight or SpotLight in the same World"
913
+ };
914
+ }
915
+ function resolveIntegratedVolumeConsumer(resource, consumer) {
916
+ return { consumer, resource };
917
+ }
918
+
919
+ // src/reflection/filter.ts
920
+ function createProbeFilterState(input) {
921
+ return {
922
+ probeIndex: input.probeIndex,
923
+ faceCount: input.faceCount ?? 6,
924
+ mipCount: input.mipCount,
925
+ cursor: 0,
926
+ activeGeneration: input.activeGeneration ?? 0
927
+ };
928
+ }
929
+ function advanceProbeFilter(state) {
930
+ const total = state.faceCount * state.mipCount;
931
+ if (state.cursor >= total) return void 0;
932
+ return {
933
+ probeIndex: state.probeIndex,
934
+ faceIndex: state.cursor % state.faceCount,
935
+ mipLevel: Math.floor(state.cursor / state.faceCount)
936
+ };
937
+ }
938
+ function commitProbeFilterStep(state, success) {
939
+ if (!success) return state;
940
+ const nextCursor = state.cursor + 1;
941
+ const complete = nextCursor >= state.faceCount * state.mipCount;
942
+ return {
943
+ ...state,
944
+ cursor: nextCursor,
945
+ activeGeneration: complete ? state.activeGeneration + 1 : state.activeGeneration
946
+ };
947
+ }
948
+ function probeFilterIsSteady(state) {
949
+ return state.cursor >= state.faceCount * state.mipCount;
950
+ }
951
+ function normalize(vector) {
952
+ const length = Math.hypot(vector[0], vector[1], vector[2]);
953
+ if (length === 0) return [0, 0, 1];
954
+ return [vector[0] / length, vector[1] / length, vector[2] / length];
955
+ }
956
+ function boxProjectReflectionDirection(direction, boxCenter, halfExtents, point) {
957
+ const ray = normalize(direction);
958
+ let distance = Number.POSITIVE_INFINITY;
959
+ for (let axis = 0; axis < 3; axis += 1) {
960
+ const component = ray[axis] ?? 0;
961
+ if (component === 0) continue;
962
+ const extent = halfExtents[axis] ?? 0;
963
+ const edge = component > 0 ? (boxCenter[axis] ?? 0) + extent : (boxCenter[axis] ?? 0) - extent;
964
+ const candidate = (edge - (point[axis] ?? 0)) / component;
965
+ if (candidate > 0) distance = Math.min(distance, candidate);
966
+ }
967
+ if (!Number.isFinite(distance)) return ray;
968
+ return normalize([
969
+ (point[0] ?? 0) + ray[0] * distance - (boxCenter[0] ?? 0),
970
+ (point[1] ?? 0) + ray[1] * distance - (boxCenter[1] ?? 0),
971
+ (point[2] ?? 0) + ray[2] * distance - (boxCenter[2] ?? 0)
972
+ ]);
973
+ }
974
+
975
+ // src/reflection/gpu-table.ts
976
+ function buildReflectionProbeTable(rows) {
977
+ const ordered = rows.slice().sort((a, b) => a.index - b.index);
978
+ const indices = /* @__PURE__ */ new Map();
979
+ for (const row of ordered) {
980
+ if (row.primitiveKey !== void 0) indices.set(row.primitiveKey, row.index);
981
+ }
982
+ const indexFor = (primitiveKey2) => indices.get(primitiveKey2);
983
+ return {
984
+ rows: Object.freeze(ordered),
985
+ cpuIndexByPrimitive: indexFor,
986
+ gpuIndexByPrimitive: indexFor
987
+ };
988
+ }
989
+ function reflectionProbeTableBytes(table) {
990
+ return table.rows.length * 48;
991
+ }
992
+
993
+ // src/reflection/projection.ts
994
+ var REFLECTION_PROBE_BYTES_PER_TEXEL = 8;
995
+ var DEFAULT_REFLECTION_PROBE_LIMITS = Object.freeze({
996
+ maxProbes: 16,
997
+ maxResolution: 256,
998
+ maxBytes: 64 * 1024 * 1024
999
+ });
1000
+ function validateReflectionProbeInput(input) {
1001
+ for (let index = 0; index < input.halfExtents.length; index += 1) {
1002
+ const value = input.halfExtents[index];
1003
+ if (value === void 0 || !Number.isFinite(value) || value <= 0) {
1004
+ return {
1005
+ ok: false,
1006
+ error: {
1007
+ code: "reflection-probe-input-invalid",
1008
+ field: `halfExtents[${index}]`,
1009
+ value,
1010
+ expected: "a finite number greater than zero"
1011
+ }
1012
+ };
1013
+ }
1014
+ }
1015
+ if (!Number.isFinite(input.priority)) {
1016
+ return {
1017
+ ok: false,
1018
+ error: {
1019
+ code: "reflection-probe-input-invalid",
1020
+ field: "priority",
1021
+ value: input.priority,
1022
+ expected: "a finite number"
1023
+ }
1024
+ };
1025
+ }
1026
+ if (!Number.isFinite(input.intensity) || input.intensity < 0) {
1027
+ return {
1028
+ ok: false,
1029
+ error: {
1030
+ code: "reflection-probe-input-invalid",
1031
+ field: "intensity",
1032
+ value: input.intensity,
1033
+ expected: "a finite number greater than or equal to zero"
1034
+ }
1035
+ };
1036
+ }
1037
+ if (!Number.isInteger(input.resolution) || input.resolution <= 0) {
1038
+ return {
1039
+ ok: false,
1040
+ error: {
1041
+ code: "reflection-probe-input-invalid",
1042
+ field: "resolution",
1043
+ value: input.resolution,
1044
+ expected: "a positive integer"
1045
+ }
1046
+ };
1047
+ }
1048
+ return { ok: true };
1049
+ }
1050
+ function estimateReflectionProbeBytes(resolution, limits = DEFAULT_REFLECTION_PROBE_LIMITS) {
1051
+ const maxResolution = limits.maxResolution ?? DEFAULT_REFLECTION_PROBE_LIMITS.maxResolution;
1052
+ const clamped = Math.min(resolution, maxResolution);
1053
+ let mipTexels = 0;
1054
+ for (let size = clamped; size >= 1; size = Math.floor(size / 2)) {
1055
+ mipTexels += size * size;
1056
+ }
1057
+ return mipTexels * 6 * REFLECTION_PROBE_BYTES_PER_TEXEL;
1058
+ }
1059
+ function admitReflectionProbe(input, current, limits = DEFAULT_REFLECTION_PROBE_LIMITS) {
1060
+ const maxProbes = limits.maxProbes ?? DEFAULT_REFLECTION_PROBE_LIMITS.maxProbes;
1061
+ const maxBytes = limits.maxBytes ?? DEFAULT_REFLECTION_PROBE_LIMITS.maxBytes;
1062
+ const maxResolution = limits.maxResolution ?? DEFAULT_REFLECTION_PROBE_LIMITS.maxResolution;
1063
+ const requested = estimateReflectionProbeBytes(input.resolution, { maxResolution });
1064
+ if (input.resolution > maxResolution || current.acceptedCount >= maxProbes || current.acceptedBytes + requested > maxBytes) {
1065
+ return {
1066
+ ok: false,
1067
+ error: {
1068
+ code: "reflection-probe-budget-exceeded",
1069
+ requested,
1070
+ activeCount: current.acceptedCount,
1071
+ activeBytes: current.acceptedBytes,
1072
+ maxProbes,
1073
+ maxBytes
1074
+ }
1075
+ };
1076
+ }
1077
+ return { ok: true, acceptedBytes: current.acceptedBytes + requested };
1078
+ }
1079
+ function contains(fact, point) {
1080
+ return fact.halfExtents.every((extent, axis) => {
1081
+ const center = fact.center[axis] ?? 0;
1082
+ const coordinate = point[axis] ?? 0;
1083
+ return Math.abs(coordinate - center) <= extent;
1084
+ });
1085
+ }
1086
+ function normalizedDistance(fact, point) {
1087
+ let squared = 0;
1088
+ for (let axis = 0; axis < 3; axis += 1) {
1089
+ const delta = (point[axis] ?? 0) - (fact.center[axis] ?? 0);
1090
+ const extent = fact.halfExtents[axis] ?? 1;
1091
+ squared += (delta / extent) ** 2;
1092
+ }
1093
+ return squared;
1094
+ }
1095
+ function isBetter(candidate, current, facts) {
1096
+ const candidateFact = facts.find(
1097
+ (fact) => fact.worldId === candidate.worldId && fact.entityKey === candidate.entityKey
1098
+ );
1099
+ const currentFact = facts.find(
1100
+ (fact) => fact.worldId === current.worldId && fact.entityKey === current.entityKey
1101
+ );
1102
+ if (candidateFact === void 0 || currentFact === void 0) return false;
1103
+ if (candidateFact.priority !== currentFact.priority)
1104
+ return candidateFact.priority > currentFact.priority;
1105
+ if (candidate.normalizedDistance !== current.normalizedDistance) {
1106
+ return candidate.normalizedDistance < current.normalizedDistance;
1107
+ }
1108
+ return candidate.worldId < current.worldId || candidate.worldId === current.worldId && candidate.entityKey < current.entityKey;
1109
+ }
1110
+ function selectReflectionProbe(facts, primitiveCenter) {
1111
+ let selected;
1112
+ for (const fact of facts) {
1113
+ if (!contains(fact, primitiveCenter)) continue;
1114
+ const candidate = {
1115
+ kind: "probe",
1116
+ worldId: fact.worldId,
1117
+ entityKey: fact.entityKey,
1118
+ normalizedDistance: normalizedDistance(fact, primitiveCenter)
1119
+ };
1120
+ if (selected === void 0 || isBetter(candidate, selected, facts)) selected = candidate;
1121
+ }
1122
+ return selected ?? { kind: "skylight" };
1123
+ }
1124
+ function primitiveKey(worldId, entityKey) {
1125
+ return `${worldId}:${entityKey}`;
1126
+ }
1127
+ var ReflectionProbeProjection = class {
1128
+ revision = 0;
1129
+ facts = [];
1130
+ selected = /* @__PURE__ */ new Map();
1131
+ signature = "";
1132
+ scannedPrimitives = 0;
1133
+ update(facts, primitives) {
1134
+ const nextFacts = facts.slice().sort((a, b) => a.worldId - b.worldId || a.entityKey - b.entityKey);
1135
+ const signature = JSON.stringify({ facts: nextFacts, primitives });
1136
+ if (signature === this.signature) return this.snapshot();
1137
+ this.signature = signature;
1138
+ this.facts = nextFacts;
1139
+ this.selected = /* @__PURE__ */ new Map();
1140
+ for (const primitive of primitives) {
1141
+ this.selected.set(
1142
+ primitiveKey(primitive.worldId, primitive.entityKey),
1143
+ selectReflectionProbe(nextFacts, primitive.center)
1144
+ );
1145
+ }
1146
+ this.scannedPrimitives = primitives.length;
1147
+ this.revision += 1;
1148
+ return this.snapshot();
1149
+ }
1150
+ snapshot() {
1151
+ return {
1152
+ revision: this.revision,
1153
+ facts: this.facts,
1154
+ selected: this.selected,
1155
+ scannedPrimitives: this.scannedPrimitives
1156
+ };
1157
+ }
1158
+ selection(worldId, entityKey) {
1159
+ return this.selected.get(primitiveKey(worldId, entityKey)) ?? { kind: "skylight" };
1160
+ }
1161
+ };
1162
+ var VISIBILITY_CACHE = /* @__PURE__ */ new WeakMap();
1163
+ function resolveVisibility(world, hierarchy = projectHierarchy(world)) {
1164
+ const cached = VISIBILITY_CACHE.get(world);
1165
+ const changes = cached?.changes ?? createWorldProjection(world, { components: [Visibility] });
1166
+ if (cached !== void 0) {
1167
+ const evidence = changes.poll();
1168
+ if (evidence.status === "delta" && evidence.changes.length === 0 && cached.hierarchy === hierarchy) {
1169
+ return cached.snapshot;
1170
+ }
1171
+ }
1172
+ const query = world.query({ read: [Visibility] });
1173
+ const visitVisibilityRows = (visit) => {
1174
+ if (!query.ok) return;
1175
+ const spans = query.value.spans();
1176
+ if (spans.ok) {
1177
+ for (const span of spans.value) {
1178
+ const states = span.get(Visibility).state;
1179
+ for (let index = 0; index < span.length; index += 1) {
1180
+ visit(span.entities[index], states[index] ?? 0);
1181
+ }
1182
+ }
1183
+ return;
1184
+ }
1185
+ for (const row of query.value) {
1186
+ visit(row.entity, row.get(Visibility).state);
1187
+ }
1188
+ };
1189
+ let intentCount = 0;
1190
+ let hasAnyHiddenIntent = false;
1191
+ visitVisibilityRows((_entity, rawState) => {
1192
+ intentCount += 1;
1193
+ if (rawState === VisibilityStateValue.hidden) {
1194
+ hasAnyHiddenIntent = true;
1195
+ }
1196
+ });
1197
+ let resolveEntity;
1198
+ let hasIntent;
1199
+ const ensureResolver = () => {
1200
+ if (resolveEntity !== void 0) return;
1201
+ const intentByEntity = /* @__PURE__ */ new Map();
1202
+ visitVisibilityRows((entity, rawState) => {
1203
+ const intent = visibilityStateFromU32(rawState);
1204
+ if (intent !== void 0) {
1205
+ intentByEntity.set(entity, intent);
1206
+ }
1207
+ });
1208
+ hasIntent = (entity) => intentByEntity.has(entity);
1209
+ const resolved = /* @__PURE__ */ new Map();
1210
+ const resolving = /* @__PURE__ */ new Set();
1211
+ resolveEntity = (entity) => {
1212
+ const existing = resolved.get(entity);
1213
+ if (existing !== void 0) return existing;
1214
+ if (resolving.has(entity)) return void 0;
1215
+ const intent = intentByEntity.get(entity) ?? "inherited";
1216
+ resolving.add(entity);
1217
+ let result;
1218
+ if (intent === "hidden") {
1219
+ result = { intent, effective: "hidden", source: "self" };
1220
+ } else if (intent === "visible") {
1221
+ result = { intent, effective: "visible", source: "self" };
1222
+ } else {
1223
+ const parent = hierarchy.getParent(entity);
1224
+ const parentResult = parent === void 0 ? void 0 : resolveEntity?.(parent);
1225
+ result = parentResult === void 0 ? { intent, effective: "visible", source: "default" } : { intent, effective: parentResult.effective, source: "parent" };
1226
+ }
1227
+ resolving.delete(entity);
1228
+ resolved.set(entity, result);
1229
+ return result;
1230
+ };
1231
+ };
1232
+ const snapshot = {
1233
+ diagnostics: hierarchy.diagnostics,
1234
+ hasAnyIntent: intentCount > 0,
1235
+ hasAnyHiddenIntent,
1236
+ get(entity) {
1237
+ ensureResolver();
1238
+ return hasIntent?.(entity) ? resolveEntity?.(entity) : void 0;
1239
+ },
1240
+ effective(entity) {
1241
+ ensureResolver();
1242
+ return resolveEntity?.(entity)?.effective ?? "visible";
1243
+ }
1244
+ };
1245
+ VISIBILITY_CACHE.set(world, { changes, hierarchy, snapshot });
1246
+ return snapshot;
1247
+ }
1248
+ var VolumetricFog = defineComponent("VolumetricFog", {
1249
+ light: { type: "entity" },
1250
+ spotLight: { type: "entity" },
1251
+ density: { type: "shared<TextureAsset>", simulationTransient: true },
1252
+ boundsMin: { type: "array<f32, 3>" },
1253
+ boundsMax: { type: "array<f32, 3>" },
1254
+ extinction: { type: "array<f32, 3>" },
1255
+ albedo: { type: "array<f32, 3>" },
1256
+ emission: { type: "array<f32, 3>" },
1257
+ anisotropy: { type: "f32", default: 0 },
1258
+ maxDistance: { type: "f32", default: 100 }
1259
+ });
1260
+ function finiteVector(value) {
1261
+ return value.every(Number.isFinite);
1262
+ }
1263
+ function validBounds(bounds) {
1264
+ if (!finiteVector(bounds.min) || !finiteVector(bounds.max)) return false;
1265
+ return bounds.max.every((value, index) => value > (bounds.min[index] ?? value));
1266
+ }
1267
+ function validRange(value, min, max = Infinity) {
1268
+ return value.every((entry) => Number.isFinite(entry) && entry >= min && entry <= max);
1269
+ }
1270
+ function validateVolumetricFog(input) {
1271
+ const ownerCount = input.ownerCount ?? 1;
1272
+ if (ownerCount !== 1) return err(new VolumeOwnerConflictError(ownerCount));
1273
+ if (input.density.shape.viewDimension !== "3d" || input.density.colorSpace === "srgb") {
1274
+ return err(
1275
+ new VolumeDensityShapeMismatchError(input.density.guid, input.density.shape.viewDimension)
1276
+ );
1277
+ }
1278
+ if (!validBounds(input.bounds)) return err(new VolumeInvalidBoundsError(input.bounds));
1279
+ 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) {
1280
+ return err(new VolumeInvalidParametersError(input));
1281
+ }
1282
+ return ok({ ...input, ownerCount: 1 });
1283
+ }
1284
+ function extractVolumetricFog(inputs) {
1285
+ if (inputs.length === 0) return ok({ status: "off" });
1286
+ if (inputs.length !== 1) return err(new VolumeOwnerConflictError(inputs.length));
1287
+ const validated = validateVolumetricFog(inputs[0]);
1288
+ if (!validated.ok) return validated;
1289
+ return ok({ status: "available", fog: validated.value });
1290
+ }
1291
+
1292
+ export { ANTIALIAS_FXAA, ANTIALIAS_MSAA, ANTIALIAS_NONE, ANTIALIAS_TAA, Atmosphere, BLOOM_DISABLED, BLOOM_ENABLED, CAMERA_PROJECTION_ORTHOGRAPHIC, CAMERA_PROJECTION_PERSPECTIVE, CUBE_CAMERA_FACE_ORDER, CUBE_CAMERA_UPDATE_CONTINUOUS, CUBE_CAMERA_UPDATE_ONCE, CUBE_CAMERA_UPDATE_ON_DEMAND, Camera, CubeCamera, DEFAULT_REFLECTION_PROBE_LIMITS, DirectionalLight, DirectionalShadowFilterValue, Fog, Instances, Layer, Lines, MeshFilter, MeshRenderer, MotionBlur, PointLight, PointLightShadow, PointShapeValue, Points, PostProcessParams, REFLECTION_PROBE_UPDATE_CONTINUOUS, REFLECTION_PROBE_UPDATE_ONCE, REFLECTION_PROBE_UPDATE_ON_CHANGE, ReflectionProbe, ReflectionProbeProjection, SKYBOX_MODE_CUBEMAP, SceneInstance, SkyboxBackground, Skylight, SortKey, SpotLight, TONEMAP_ACES_FILMIC, TONEMAP_AGX, TONEMAP_CINEON, TONEMAP_LINEAR, TONEMAP_NEUTRAL, TONEMAP_NONE, TONEMAP_REINHARD, TONEMAP_REINHARD_EXTENDED, Visibility, VisibilityStateValue, VolumetricFog, admitPointsLines, admitReflectionProbe, advanceProbeFilter, antialiasFromF32, bloomEnabledFromF32, boxProjectReflectionDirection, buildCubeCameraFaceViews, buildReflectionProbeTable, cameraProjectionFromF32, commitProbeFilterStep, createProbeFilterState, cubeCameraUpdateIntentFromF32, cubeCameraUpdateIntentToF32, directionalShadowQualityFromF32, estimateReflectionProbeBytes, extractVolumetricFog, hasVolumetricFogCapability, inspectPointShadow, inspectVolumetricFog, orthographic, perspective, pointShapeFromU32, probeFilterIsSteady, projectMeshMaterialBindingObservation, reflectionProbeTableBytes, reflectionProbeUpdateIntentFromF32, reflectionProbeUpdateIntentToF32, resolveIntegratedVolumeConsumer, resolveMeshMaterialBindings, resolveSelectedVolumetricLight, resolveVisibility, resolveVolumetricFogLightPair, selectReflectionProbe, summarizeMeshMaterialBindings, tonemapFromF32, tonemapToU32, validateReflectionProbeInput, validateVolumetricFog, visibilityStateFromU32 };
1293
+ //# sourceMappingURL=chunk-FS2KNUQH.mjs.map
1294
+ //# sourceMappingURL=chunk-FS2KNUQH.mjs.map