@fps-games/engine 0.0.1-beta.1 → 0.0.1-beta.11

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 (326) hide show
  1. package/dist/asset-resource.d.ts +39 -0
  2. package/dist/asset-resource.d.ts.map +1 -0
  3. package/dist/asset-resource.js +121 -0
  4. package/dist/asset-resource.js.map +1 -0
  5. package/dist/babylon/camera.d.ts +25 -0
  6. package/dist/babylon/camera.d.ts.map +1 -0
  7. package/dist/babylon/camera.js +149 -0
  8. package/dist/babylon/camera.js.map +1 -0
  9. package/dist/babylon/frame-pump.d.ts +27 -0
  10. package/dist/babylon/frame-pump.d.ts.map +1 -0
  11. package/dist/babylon/frame-pump.js +90 -0
  12. package/dist/babylon/frame-pump.js.map +1 -0
  13. package/dist/babylon/game-world-plugin-lifecycle.d.ts +26 -0
  14. package/dist/babylon/game-world-plugin-lifecycle.d.ts.map +1 -0
  15. package/dist/babylon/game-world-plugin-lifecycle.js +257 -0
  16. package/dist/babylon/game-world-plugin-lifecycle.js.map +1 -0
  17. package/dist/babylon/lighting-controller.d.ts +27 -0
  18. package/dist/babylon/lighting-controller.d.ts.map +1 -0
  19. package/dist/babylon/lighting-controller.js +272 -0
  20. package/dist/babylon/lighting-controller.js.map +1 -0
  21. package/dist/babylon/plugin-contract.d.ts +15 -0
  22. package/dist/babylon/plugin-contract.d.ts.map +1 -0
  23. package/dist/babylon/plugin-contract.js +9 -0
  24. package/dist/babylon/plugin-contract.js.map +1 -0
  25. package/dist/babylon/renderer/brdf-texture-settlement.d.ts +7 -0
  26. package/dist/babylon/renderer/brdf-texture-settlement.d.ts.map +1 -0
  27. package/dist/babylon/renderer/brdf-texture-settlement.js +29 -0
  28. package/dist/babylon/renderer/brdf-texture-settlement.js.map +1 -0
  29. package/dist/babylon/renderer/environment/environment-texture-controller.d.ts +7 -0
  30. package/dist/babylon/renderer/environment/environment-texture-controller.d.ts.map +1 -0
  31. package/dist/babylon/renderer/environment/environment-texture-controller.js +357 -0
  32. package/dist/babylon/renderer/environment/environment-texture-controller.js.map +1 -0
  33. package/dist/babylon/renderer/environment/index.d.ts +4 -0
  34. package/dist/babylon/renderer/environment/index.d.ts.map +1 -0
  35. package/dist/babylon/renderer/environment/index.js +4 -0
  36. package/dist/babylon/renderer/environment/index.js.map +1 -0
  37. package/dist/babylon/renderer/environment/profile.d.ts +3 -0
  38. package/dist/babylon/renderer/environment/profile.d.ts.map +1 -0
  39. package/dist/babylon/renderer/environment/profile.js +27 -0
  40. package/dist/babylon/renderer/environment/profile.js.map +1 -0
  41. package/dist/babylon/renderer/environment/types.d.ts +24 -0
  42. package/dist/babylon/renderer/environment/types.d.ts.map +1 -0
  43. package/dist/babylon/renderer/environment/types.js +6 -0
  44. package/dist/babylon/renderer/environment/types.js.map +1 -0
  45. package/dist/babylon/renderer/index.d.ts +25 -0
  46. package/dist/babylon/renderer/index.d.ts.map +1 -0
  47. package/dist/babylon/renderer/index.js +25 -0
  48. package/dist/babylon/renderer/index.js.map +1 -0
  49. package/dist/babylon/renderer/lighting/directional-light-angles.d.ts +17 -0
  50. package/dist/babylon/renderer/lighting/directional-light-angles.d.ts.map +1 -0
  51. package/dist/babylon/renderer/lighting/directional-light-angles.js +67 -0
  52. package/dist/babylon/renderer/lighting/directional-light-angles.js.map +1 -0
  53. package/dist/babylon/renderer/material-owner-candidates.d.ts +19 -0
  54. package/dist/babylon/renderer/material-owner-candidates.d.ts.map +1 -0
  55. package/dist/babylon/renderer/material-owner-candidates.js +212 -0
  56. package/dist/babylon/renderer/material-owner-candidates.js.map +1 -0
  57. package/dist/babylon/renderer/materials/index.d.ts +3 -0
  58. package/dist/babylon/renderer/materials/index.d.ts.map +1 -0
  59. package/dist/babylon/renderer/materials/index.js +3 -0
  60. package/dist/babylon/renderer/materials/index.js.map +1 -0
  61. package/dist/babylon/renderer/materials/material-runtime.d.ts +29 -0
  62. package/dist/babylon/renderer/materials/material-runtime.d.ts.map +1 -0
  63. package/dist/babylon/renderer/materials/material-runtime.js +660 -0
  64. package/dist/babylon/renderer/materials/material-runtime.js.map +1 -0
  65. package/dist/babylon/renderer/materials/material-slot-owner.d.ts +42 -0
  66. package/dist/babylon/renderer/materials/material-slot-owner.d.ts.map +1 -0
  67. package/dist/babylon/renderer/materials/material-slot-owner.js +318 -0
  68. package/dist/babylon/renderer/materials/material-slot-owner.js.map +1 -0
  69. package/dist/babylon/renderer/outlines/babylon-default/apply.d.ts +4 -0
  70. package/dist/babylon/renderer/outlines/babylon-default/apply.d.ts.map +1 -0
  71. package/dist/babylon/renderer/outlines/babylon-default/apply.js +45 -0
  72. package/dist/babylon/renderer/outlines/babylon-default/apply.js.map +1 -0
  73. package/dist/babylon/renderer/outlines/babylon-default/index.d.ts +3 -0
  74. package/dist/babylon/renderer/outlines/babylon-default/index.d.ts.map +1 -0
  75. package/dist/babylon/renderer/outlines/babylon-default/index.js +2 -0
  76. package/dist/babylon/renderer/outlines/babylon-default/index.js.map +1 -0
  77. package/dist/babylon/renderer/outlines/babylon-default/types.d.ts +7 -0
  78. package/dist/babylon/renderer/outlines/babylon-default/types.d.ts.map +1 -0
  79. package/dist/babylon/renderer/outlines/babylon-default/types.js +2 -0
  80. package/dist/babylon/renderer/outlines/babylon-default/types.js.map +1 -0
  81. package/dist/babylon/renderer/outlines/index.d.ts +4 -0
  82. package/dist/babylon/renderer/outlines/index.d.ts.map +1 -0
  83. package/dist/babylon/renderer/outlines/index.js +3 -0
  84. package/dist/babylon/renderer/outlines/index.js.map +1 -0
  85. package/dist/babylon/renderer/outlines/runtime-override.d.ts +36 -0
  86. package/dist/babylon/renderer/outlines/runtime-override.d.ts.map +1 -0
  87. package/dist/babylon/renderer/outlines/runtime-override.js +131 -0
  88. package/dist/babylon/renderer/outlines/runtime-override.js.map +1 -0
  89. package/dist/babylon/renderer/outlines/types.d.ts +7 -0
  90. package/dist/babylon/renderer/outlines/types.d.ts.map +1 -0
  91. package/dist/babylon/renderer/outlines/types.js +2 -0
  92. package/dist/babylon/renderer/outlines/types.js.map +1 -0
  93. package/dist/babylon/renderer/placement/index.d.ts +2 -0
  94. package/dist/babylon/renderer/placement/index.d.ts.map +1 -0
  95. package/dist/babylon/renderer/placement/index.js +2 -0
  96. package/dist/babylon/renderer/placement/index.js.map +1 -0
  97. package/dist/babylon/renderer/placement/runtime-drop-position.d.ts +24 -0
  98. package/dist/babylon/renderer/placement/runtime-drop-position.d.ts.map +1 -0
  99. package/dist/babylon/renderer/placement/runtime-drop-position.js +63 -0
  100. package/dist/babylon/renderer/placement/runtime-drop-position.js.map +1 -0
  101. package/dist/babylon/renderer/post-process/babylon-default-pipeline.d.ts +7 -0
  102. package/dist/babylon/renderer/post-process/babylon-default-pipeline.d.ts.map +1 -0
  103. package/dist/babylon/renderer/post-process/babylon-default-pipeline.js +301 -0
  104. package/dist/babylon/renderer/post-process/babylon-default-pipeline.js.map +1 -0
  105. package/dist/babylon/renderer/post-process/index.d.ts +5 -0
  106. package/dist/babylon/renderer/post-process/index.d.ts.map +1 -0
  107. package/dist/babylon/renderer/post-process/index.js +5 -0
  108. package/dist/babylon/renderer/post-process/index.js.map +1 -0
  109. package/dist/babylon/renderer/post-process/profile.d.ts +5 -0
  110. package/dist/babylon/renderer/post-process/profile.d.ts.map +1 -0
  111. package/dist/babylon/renderer/post-process/profile.js +155 -0
  112. package/dist/babylon/renderer/post-process/profile.js.map +1 -0
  113. package/dist/babylon/renderer/post-process/types.d.ts +81 -0
  114. package/dist/babylon/renderer/post-process/types.d.ts.map +1 -0
  115. package/dist/babylon/renderer/post-process/types.js +24 -0
  116. package/dist/babylon/renderer/post-process/types.js.map +1 -0
  117. package/dist/babylon/renderer/post-process/volume-stack.d.ts +4 -0
  118. package/dist/babylon/renderer/post-process/volume-stack.d.ts.map +1 -0
  119. package/dist/babylon/renderer/post-process/volume-stack.js +105 -0
  120. package/dist/babylon/renderer/post-process/volume-stack.js.map +1 -0
  121. package/dist/babylon/renderer/rendering/image-node.d.ts +35 -0
  122. package/dist/babylon/renderer/rendering/image-node.d.ts.map +1 -0
  123. package/dist/babylon/renderer/rendering/image-node.js +39 -0
  124. package/dist/babylon/renderer/rendering/image-node.js.map +1 -0
  125. package/dist/babylon/renderer/rendering/index.d.ts +3 -0
  126. package/dist/babylon/renderer/rendering/index.d.ts.map +1 -0
  127. package/dist/babylon/renderer/rendering/index.js +3 -0
  128. package/dist/babylon/renderer/rendering/index.js.map +1 -0
  129. package/dist/babylon/renderer/rendering/node-rendering.d.ts +19 -0
  130. package/dist/babylon/renderer/rendering/node-rendering.d.ts.map +1 -0
  131. package/dist/babylon/renderer/rendering/node-rendering.js +66 -0
  132. package/dist/babylon/renderer/rendering/node-rendering.js.map +1 -0
  133. package/dist/babylon/renderer/shadows/blob-shadow-shaders.d.ts +5 -0
  134. package/dist/babylon/renderer/shadows/blob-shadow-shaders.d.ts.map +1 -0
  135. package/dist/babylon/renderer/shadows/blob-shadow-shaders.js +46 -0
  136. package/dist/babylon/renderer/shadows/blob-shadow-shaders.js.map +1 -0
  137. package/dist/babylon/renderer/shadows/blob-shadow-system.d.ts +5 -0
  138. package/dist/babylon/renderer/shadows/blob-shadow-system.d.ts.map +1 -0
  139. package/dist/babylon/renderer/shadows/blob-shadow-system.js +402 -0
  140. package/dist/babylon/renderer/shadows/blob-shadow-system.js.map +1 -0
  141. package/dist/babylon/renderer/shadows/dynamic-shadow-system.d.ts +84 -0
  142. package/dist/babylon/renderer/shadows/dynamic-shadow-system.d.ts.map +1 -0
  143. package/dist/babylon/renderer/shadows/dynamic-shadow-system.js +566 -0
  144. package/dist/babylon/renderer/shadows/dynamic-shadow-system.js.map +1 -0
  145. package/dist/babylon/renderer/shadows/planar-shadow-shaders.d.ts +7 -0
  146. package/dist/babylon/renderer/shadows/planar-shadow-shaders.d.ts.map +1 -0
  147. package/dist/babylon/renderer/shadows/planar-shadow-shaders.js +316 -0
  148. package/dist/babylon/renderer/shadows/planar-shadow-shaders.js.map +1 -0
  149. package/dist/babylon/renderer/shadows/planar-shadow-system.d.ts +5 -0
  150. package/dist/babylon/renderer/shadows/planar-shadow-system.d.ts.map +1 -0
  151. package/dist/babylon/renderer/shadows/planar-shadow-system.js +1562 -0
  152. package/dist/babylon/renderer/shadows/planar-shadow-system.js.map +1 -0
  153. package/dist/babylon/renderer/shadows/shadow-generator-shaders.d.ts +14 -0
  154. package/dist/babylon/renderer/shadows/shadow-generator-shaders.d.ts.map +1 -0
  155. package/dist/babylon/renderer/shadows/shadow-generator-shaders.js +14 -0
  156. package/dist/babylon/renderer/shadows/shadow-generator-shaders.js.map +1 -0
  157. package/dist/babylon/renderer/shadows/shadow-map-experiment-depth-backend.d.ts +68 -0
  158. package/dist/babylon/renderer/shadows/shadow-map-experiment-depth-backend.d.ts.map +1 -0
  159. package/dist/babylon/renderer/shadows/shadow-map-experiment-depth-backend.js +829 -0
  160. package/dist/babylon/renderer/shadows/shadow-map-experiment-depth-backend.js.map +1 -0
  161. package/dist/babylon/renderer/shadows/shadow-map-experiment-system.d.ts +148 -0
  162. package/dist/babylon/renderer/shadows/shadow-map-experiment-system.d.ts.map +1 -0
  163. package/dist/babylon/renderer/shadows/shadow-map-experiment-system.js +4529 -0
  164. package/dist/babylon/renderer/shadows/shadow-map-experiment-system.js.map +1 -0
  165. package/dist/babylon/renderer/shadows/static-projected-shadow-artifact-system.d.ts +4 -0
  166. package/dist/babylon/renderer/shadows/static-projected-shadow-artifact-system.d.ts.map +1 -0
  167. package/dist/babylon/renderer/shadows/static-projected-shadow-artifact-system.js +645 -0
  168. package/dist/babylon/renderer/shadows/static-projected-shadow-artifact-system.js.map +1 -0
  169. package/dist/babylon/renderer/shadows/static-projected-shadow-atlas.d.ts +61 -0
  170. package/dist/babylon/renderer/shadows/static-projected-shadow-atlas.d.ts.map +1 -0
  171. package/dist/babylon/renderer/shadows/static-projected-shadow-atlas.js +382 -0
  172. package/dist/babylon/renderer/shadows/static-projected-shadow-atlas.js.map +1 -0
  173. package/dist/babylon/renderer/shadows/static-projected-shadow-bake.d.ts +47 -0
  174. package/dist/babylon/renderer/shadows/static-projected-shadow-bake.d.ts.map +1 -0
  175. package/dist/babylon/renderer/shadows/static-projected-shadow-bake.js +285 -0
  176. package/dist/babylon/renderer/shadows/static-projected-shadow-bake.js.map +1 -0
  177. package/dist/babylon/renderer/shadows/static-projected-shadow-baker.d.ts +88 -0
  178. package/dist/babylon/renderer/shadows/static-projected-shadow-baker.d.ts.map +1 -0
  179. package/dist/babylon/renderer/shadows/static-projected-shadow-baker.js +830 -0
  180. package/dist/babylon/renderer/shadows/static-projected-shadow-baker.js.map +1 -0
  181. package/dist/babylon/renderer/shadows/static-projected-shadow-options.d.ts +3 -0
  182. package/dist/babylon/renderer/shadows/static-projected-shadow-options.d.ts.map +1 -0
  183. package/dist/babylon/renderer/shadows/static-projected-shadow-options.js +60 -0
  184. package/dist/babylon/renderer/shadows/static-projected-shadow-options.js.map +1 -0
  185. package/dist/babylon/renderer/shadows/static-projected-shadow-rendering.d.ts +27 -0
  186. package/dist/babylon/renderer/shadows/static-projected-shadow-rendering.d.ts.map +1 -0
  187. package/dist/babylon/renderer/shadows/static-projected-shadow-rendering.js +220 -0
  188. package/dist/babylon/renderer/shadows/static-projected-shadow-rendering.js.map +1 -0
  189. package/dist/babylon/renderer/shadows/types.d.ts +421 -0
  190. package/dist/babylon/renderer/shadows/types.d.ts.map +1 -0
  191. package/dist/babylon/renderer/shadows/types.js +127 -0
  192. package/dist/babylon/renderer/shadows/types.js.map +1 -0
  193. package/dist/babylon/renderer/world-rendering-controller.d.ts +29 -0
  194. package/dist/babylon/renderer/world-rendering-controller.d.ts.map +1 -0
  195. package/dist/babylon/renderer/world-rendering-controller.js +178 -0
  196. package/dist/babylon/renderer/world-rendering-controller.js.map +1 -0
  197. package/dist/babylon/renderer-controller.d.ts +93 -0
  198. package/dist/babylon/renderer-controller.d.ts.map +1 -0
  199. package/dist/babylon/renderer-controller.js +706 -0
  200. package/dist/babylon/renderer-controller.js.map +1 -0
  201. package/dist/babylon/scene-renderer.d.ts +20 -0
  202. package/dist/babylon/scene-renderer.d.ts.map +1 -0
  203. package/dist/babylon/scene-renderer.js +60 -0
  204. package/dist/babylon/scene-renderer.js.map +1 -0
  205. package/dist/babylon.d.ts +8 -0
  206. package/dist/babylon.d.ts.map +1 -1
  207. package/dist/babylon.js +8 -0
  208. package/dist/babylon.js.map +1 -1
  209. package/dist/build-info.json +4 -4
  210. package/dist/camera.d.ts +25 -0
  211. package/dist/camera.d.ts.map +1 -0
  212. package/dist/camera.js +68 -0
  213. package/dist/camera.js.map +1 -0
  214. package/dist/compiled-scene-data.d.ts +23 -0
  215. package/dist/compiled-scene-data.d.ts.map +1 -0
  216. package/dist/compiled-scene-data.js +100 -0
  217. package/dist/compiled-scene-data.js.map +1 -0
  218. package/dist/frame.d.ts +32 -0
  219. package/dist/frame.d.ts.map +1 -0
  220. package/dist/frame.js +54 -0
  221. package/dist/frame.js.map +1 -0
  222. package/dist/ground-decal.d.ts +22 -0
  223. package/dist/ground-decal.d.ts.map +1 -0
  224. package/dist/ground-decal.js +48 -0
  225. package/dist/ground-decal.js.map +1 -0
  226. package/dist/index.d.ts +17 -1
  227. package/dist/index.d.ts.map +1 -1
  228. package/dist/index.js +17 -1
  229. package/dist/index.js.map +1 -1
  230. package/dist/lighting.d.ts +66 -0
  231. package/dist/lighting.d.ts.map +1 -0
  232. package/dist/lighting.js +197 -0
  233. package/dist/lighting.js.map +1 -0
  234. package/dist/material-owner.d.ts +128 -0
  235. package/dist/material-owner.d.ts.map +1 -0
  236. package/dist/material-owner.js +434 -0
  237. package/dist/material-owner.js.map +1 -0
  238. package/dist/material-profile.d.ts +62 -0
  239. package/dist/material-profile.d.ts.map +1 -0
  240. package/dist/material-profile.js +132 -0
  241. package/dist/material-profile.js.map +1 -0
  242. package/dist/rendering/index.d.ts +14 -0
  243. package/dist/rendering/index.d.ts.map +1 -0
  244. package/dist/rendering/index.js +12 -0
  245. package/dist/rendering/index.js.map +1 -0
  246. package/dist/rendering/rendering-profile-types.d.ts +322 -0
  247. package/dist/rendering/rendering-profile-types.d.ts.map +1 -0
  248. package/dist/rendering/rendering-profile-types.js +2 -0
  249. package/dist/rendering/rendering-profile-types.js.map +1 -0
  250. package/dist/rendering/rendering-profile-validation.d.ts +2 -0
  251. package/dist/rendering/rendering-profile-validation.d.ts.map +1 -0
  252. package/dist/rendering/rendering-profile-validation.js +364 -0
  253. package/dist/rendering/rendering-profile-validation.js.map +1 -0
  254. package/dist/rendering/rendering-profile-values.d.ts +19 -0
  255. package/dist/rendering/rendering-profile-values.d.ts.map +1 -0
  256. package/dist/rendering/rendering-profile-values.js +79 -0
  257. package/dist/rendering/rendering-profile-values.js.map +1 -0
  258. package/dist/rendering/rendering-profile.d.ts +15 -0
  259. package/dist/rendering/rendering-profile.d.ts.map +1 -0
  260. package/dist/rendering/rendering-profile.js +442 -0
  261. package/dist/rendering/rendering-profile.js.map +1 -0
  262. package/dist/rendering/shadow-map-experiment-depth.d.ts +63 -0
  263. package/dist/rendering/shadow-map-experiment-depth.d.ts.map +1 -0
  264. package/dist/rendering/shadow-map-experiment-depth.js +186 -0
  265. package/dist/rendering/shadow-map-experiment-depth.js.map +1 -0
  266. package/dist/rendering/shadow-map-experiment-lifecycle.d.ts +94 -0
  267. package/dist/rendering/shadow-map-experiment-lifecycle.d.ts.map +1 -0
  268. package/dist/rendering/shadow-map-experiment-lifecycle.js +407 -0
  269. package/dist/rendering/shadow-map-experiment-lifecycle.js.map +1 -0
  270. package/dist/rendering/shadow-map-experiment.d.ts +189 -0
  271. package/dist/rendering/shadow-map-experiment.d.ts.map +1 -0
  272. package/dist/rendering/shadow-map-experiment.js +549 -0
  273. package/dist/rendering/shadow-map-experiment.js.map +1 -0
  274. package/dist/rendering/shadow-quality.d.ts +5 -0
  275. package/dist/rendering/shadow-quality.d.ts.map +1 -0
  276. package/dist/rendering/shadow-quality.js +83 -0
  277. package/dist/rendering/shadow-quality.js.map +1 -0
  278. package/dist/rendering/shadow-runtime.d.ts +59 -0
  279. package/dist/rendering/shadow-runtime.d.ts.map +1 -0
  280. package/dist/rendering/shadow-runtime.js +378 -0
  281. package/dist/rendering/shadow-runtime.js.map +1 -0
  282. package/dist/rendering/shadow-settings.d.ts +15 -0
  283. package/dist/rendering/shadow-settings.d.ts.map +1 -0
  284. package/dist/rendering/shadow-settings.js +129 -0
  285. package/dist/rendering/shadow-settings.js.map +1 -0
  286. package/dist/rendering/shadow-types.d.ts +99 -0
  287. package/dist/rendering/shadow-types.d.ts.map +1 -0
  288. package/dist/rendering/shadow-types.js +5 -0
  289. package/dist/rendering/shadow-types.js.map +1 -0
  290. package/dist/rendering/shadow-validation.d.ts +3 -0
  291. package/dist/rendering/shadow-validation.d.ts.map +1 -0
  292. package/dist/rendering/shadow-validation.js +166 -0
  293. package/dist/rendering/shadow-validation.js.map +1 -0
  294. package/dist/rendering/value.d.ts +2 -0
  295. package/dist/rendering/value.d.ts.map +1 -0
  296. package/dist/rendering/value.js +6 -0
  297. package/dist/rendering/value.js.map +1 -0
  298. package/dist/runtime-data-token.d.ts +4 -0
  299. package/dist/runtime-data-token.d.ts.map +1 -0
  300. package/dist/runtime-data-token.js +4 -0
  301. package/dist/runtime-data-token.js.map +1 -0
  302. package/dist/runtime-data-types.d.ts +83 -0
  303. package/dist/runtime-data-types.d.ts.map +1 -0
  304. package/dist/runtime-data-types.js +2 -0
  305. package/dist/runtime-data-types.js.map +1 -0
  306. package/dist/runtime-data.d.ts +18 -0
  307. package/dist/runtime-data.d.ts.map +1 -0
  308. package/dist/runtime-data.js +301 -0
  309. package/dist/runtime-data.js.map +1 -0
  310. package/dist/runtime-scene-resolvers.d.ts +37 -0
  311. package/dist/runtime-scene-resolvers.d.ts.map +1 -0
  312. package/dist/runtime-scene-resolvers.js +40 -0
  313. package/dist/runtime-scene-resolvers.js.map +1 -0
  314. package/dist/scene-artifact-types.d.ts +383 -0
  315. package/dist/scene-artifact-types.d.ts.map +1 -0
  316. package/dist/scene-artifact-types.js +2 -0
  317. package/dist/scene-artifact-types.js.map +1 -0
  318. package/dist/scene-artifact-validation.d.ts +19 -0
  319. package/dist/scene-artifact-validation.d.ts.map +1 -0
  320. package/dist/scene-artifact-validation.js +1352 -0
  321. package/dist/scene-artifact-validation.js.map +1 -0
  322. package/dist/static-shadow-artifact.d.ts +131 -0
  323. package/dist/static-shadow-artifact.d.ts.map +1 -0
  324. package/dist/static-shadow-artifact.js +327 -0
  325. package/dist/static-shadow-artifact.js.map +1 -0
  326. package/package.json +11 -2
@@ -0,0 +1,1562 @@
1
+ import { Constants } from '@babylonjs/core/Engines/constants.js';
2
+ import { DirectionalLight } from '@babylonjs/core/Lights/directionalLight.js';
3
+ import { Color3, Color4 } from '@babylonjs/core/Maths/math.color.js';
4
+ import { Matrix, Vector3 } from '@babylonjs/core/Maths/math.vector.js';
5
+ import { Material } from '@babylonjs/core/Materials/material.js';
6
+ import { ShaderMaterial } from '@babylonjs/core/Materials/shaderMaterial.js';
7
+ import { StandardMaterial } from '@babylonjs/core/Materials/standardMaterial.js';
8
+ import { AbstractMesh } from '@babylonjs/core/Meshes/abstractMesh.js';
9
+ import { InstancedMesh } from '@babylonjs/core/Meshes/instancedMesh.js';
10
+ import { Mesh } from '@babylonjs/core/Meshes/mesh.js';
11
+ import { VertexData } from '@babylonjs/core/Meshes/mesh.vertexData.js';
12
+ import { TransformNode } from '@babylonjs/core/Meshes/transformNode.js';
13
+ import { VertexBuffer } from '@babylonjs/core/Buffers/buffer.js';
14
+ import { getPlanarShadowShaderAttributes, getPlanarShadowShaderSamplers, getPlanarShadowShaderUniforms, getPlanarShadowSkeletonDefines, PLANAR_SHADOW_SHADER_NAME, registerPlanarShadowShaders, } from './planar-shadow-shaders.js';
15
+ import { DEFAULT_PLANAR_SHADOW_OPTIONS, } from './types.js';
16
+ const PLANAR_SHADOW_ALPHA_INDEX = 4;
17
+ const DEFAULT_SHADOW_DIRECTION = new Vector3(-0.3, -1, -0.2).normalize();
18
+ const STANDARD_ALPHA_LUMINANCE_WEIGHTS = new Vector3(0.3, 0.59, 0.11);
19
+ const PBR_ALPHA_LUMINANCE_WEIGHTS = new Vector3(0.2126, 0.7152, 0.0722);
20
+ export function createPlanarShadowSystem(scene, directionalLight, options = {}) {
21
+ return new BabylonPlanarShadowSystem(scene, directionalLight, options);
22
+ }
23
+ class BabylonPlanarShadowSystem {
24
+ scene;
25
+ directionalLight;
26
+ options;
27
+ baseMaterial = null;
28
+ flatMaterial = null;
29
+ materials = new Set();
30
+ casters = new Map();
31
+ receivers = new Map();
32
+ syncObservers = new Map();
33
+ instancedBatches = new Map();
34
+ instancedEntriesByRoot = new Map();
35
+ renderObserver = null;
36
+ instancedBatchSyncObserver = null;
37
+ meshAddedObserver = null;
38
+ initialized = false;
39
+ casterPatterns = [];
40
+ resolvedPlaneNormal = Vector3.Up();
41
+ resolvedPlaneHeight = 0;
42
+ patternNameCache = null;
43
+ constructor(scene, directionalLight, options) {
44
+ this.scene = scene;
45
+ this.directionalLight = directionalLight;
46
+ this.options = resolvePlanarShadowOptions(options);
47
+ this.casterPatterns = [...this.options.casters.includePatterns];
48
+ }
49
+ initialize() {
50
+ if (this.initialized || !this.options.enabled)
51
+ return;
52
+ registerPlanarShadowShaders();
53
+ if (this.options.stencil.enabled)
54
+ this.setupStencil();
55
+ this.baseMaterial = this.createShadowMaterial();
56
+ this.materials.add(this.baseMaterial);
57
+ this.renderObserver = this.scene.onBeforeRenderObservable.add(() => this.updateUniforms());
58
+ this.instancedBatchSyncObserver = this.scene.onBeforeRenderObservable.add(() => this.syncInstancedBatches());
59
+ this.initialized = true;
60
+ this.refresh();
61
+ }
62
+ addCaster(mesh) {
63
+ const root = this.findCasterRoot(mesh);
64
+ if (!this.initialized || this.casters.has(root))
65
+ return false;
66
+ if (this.getInvalidShadowSourceReason(mesh))
67
+ return false;
68
+ const shadow = this.createShadowForNode(root);
69
+ if (!shadow)
70
+ return false;
71
+ this.casters.set(shadow.source, shadow);
72
+ return true;
73
+ }
74
+ removeCaster(mesh) {
75
+ const root = this.findCasterRoot(mesh);
76
+ this.disposeCaster(root);
77
+ }
78
+ addReceiver(mesh) {
79
+ this.registerReceiver(mesh, true);
80
+ }
81
+ registerReceiver(mesh, manual) {
82
+ if (!this.options.stencil.enabled || this.hasDisabledReceiver(mesh))
83
+ return;
84
+ const existing = this.receivers.get(mesh);
85
+ if (existing) {
86
+ existing.manual = existing.manual || manual;
87
+ return;
88
+ }
89
+ const originalRenderingGroup = mesh instanceof Mesh ? mesh.renderingGroupId : 0;
90
+ if (mesh instanceof Mesh)
91
+ mesh.renderingGroupId = this.options.stencil.receiverRenderingGroup;
92
+ const material = readMeshMaterial(mesh);
93
+ if (material?.stencil) {
94
+ const stencil = material.stencil;
95
+ stencil.enabled = true;
96
+ stencil.func = Constants.ALWAYS;
97
+ stencil.funcRef = 1;
98
+ stencil.funcMask = 0xff;
99
+ stencil.mask = 0xff;
100
+ stencil.opStencilDepthPass = Constants.REPLACE;
101
+ stencil.opStencilFail = Constants.KEEP;
102
+ stencil.opDepthFail = Constants.KEEP;
103
+ }
104
+ this.receivers.set(mesh, { mesh, originalRenderingGroup, material, manual });
105
+ }
106
+ removeReceiver(mesh) {
107
+ const info = this.receivers.get(mesh);
108
+ if (!info)
109
+ return;
110
+ if (mesh instanceof Mesh)
111
+ mesh.renderingGroupId = info.originalRenderingGroup;
112
+ if (info.material?.stencil)
113
+ info.material.stencil.enabled = false;
114
+ this.receivers.delete(mesh);
115
+ }
116
+ enableCasterAutoDetection(patterns) {
117
+ this.options = {
118
+ ...this.options,
119
+ casters: {
120
+ ...this.options.casters,
121
+ autoDetectAll: false,
122
+ includePatterns: [...patterns],
123
+ },
124
+ };
125
+ this.casterPatterns = [...patterns];
126
+ this.refresh();
127
+ this.ensureMeshObserver();
128
+ }
129
+ enableAutoDetectionForAll() {
130
+ this.options = {
131
+ ...this.options,
132
+ casters: {
133
+ ...this.options.casters,
134
+ autoDetectAll: true,
135
+ },
136
+ };
137
+ this.casterPatterns = [];
138
+ this.refresh();
139
+ this.ensureMeshObserver();
140
+ }
141
+ refresh() {
142
+ if (!this.initialized)
143
+ return;
144
+ const previousPatternNameCache = this.patternNameCache;
145
+ this.patternNameCache = new WeakMap();
146
+ try {
147
+ this.pruneDisposedCasters();
148
+ this.refreshReceivers();
149
+ if (this.options.casters.autoDetectAll || this.casterPatterns.length > 0) {
150
+ for (const mesh of this.scene.meshes)
151
+ this.tryAutoAddCaster(mesh);
152
+ }
153
+ if (this.options.casters.autoDetectAll || this.casterPatterns.length > 0 || this.options.receivers.patterns.length > 0) {
154
+ this.ensureMeshObserver();
155
+ }
156
+ }
157
+ finally {
158
+ this.patternNameCache = previousPatternNameCache;
159
+ }
160
+ }
161
+ setOptions(options) {
162
+ const wasEnabled = this.options.enabled;
163
+ const requiredUniqueMaterials = this.requiresPerShadowMaterial();
164
+ const previousStrategy = this.options.projection.strategy;
165
+ this.options = resolvePlanarShadowOptions(options, this.options);
166
+ this.casterPatterns = [...this.options.casters.includePatterns];
167
+ const requiresUniqueMaterials = this.requiresPerShadowMaterial();
168
+ const strategyChanged = previousStrategy !== this.options.projection.strategy;
169
+ if (!wasEnabled && this.options.enabled)
170
+ this.initialize();
171
+ else if (wasEnabled && !this.options.enabled)
172
+ this.dispose();
173
+ else {
174
+ if (requiredUniqueMaterials !== requiresUniqueMaterials || strategyChanged)
175
+ this.rebuildCasters();
176
+ this.refresh();
177
+ }
178
+ }
179
+ getOptions() {
180
+ return cloneResolvedOptions(this.options);
181
+ }
182
+ getCasterCount() {
183
+ return this.casters.size;
184
+ }
185
+ getReceiverCount() {
186
+ return this.receivers.size;
187
+ }
188
+ getCasterDebugInfo() {
189
+ return [...this.casters.values()].map((info) => ({
190
+ sourceName: info.source.name,
191
+ sourceMeshNames: [...info.sourceMeshNames],
192
+ sourceMeshUniqueIds: [...info.sourceMeshUniqueIds],
193
+ shadowName: info.shadow.name,
194
+ strategy: info.strategy,
195
+ hasSkeleton: info.hasSkeleton,
196
+ projectedMeshCount: info.projectedMeshCount,
197
+ convexHullMeshCount: info.convexHullMeshCount,
198
+ alphaCutoutMeshCount: info.alphaCutoutMeshCount,
199
+ instancedCasterCount: info.instancedCasterCount,
200
+ batchMeshCount: info.batchMeshCount,
201
+ instancedBatchIds: [...info.instancedBatchIds],
202
+ instancedBatchNames: [...info.instancedBatchNames],
203
+ independentProjectedMeshCount: info.independentProjectedMeshCount,
204
+ visibleInstanceCount: info.visibleInstanceCount,
205
+ alphaCutoutChannels: [...info.alphaCutoutChannels],
206
+ alphaCutoutUvSets: [...info.alphaCutoutUvSets],
207
+ alphaCutoutLevels: [...info.alphaCutoutLevels],
208
+ alphaCutoutLuminanceWeights: info.alphaCutoutLuminanceWeights.map((weights) => ({
209
+ x: weights.x,
210
+ y: weights.y,
211
+ z: weights.z,
212
+ })),
213
+ fallbackReasons: [...info.fallbackReasons],
214
+ instancingFallbackReasons: [...info.instancingFallbackReasons],
215
+ }));
216
+ }
217
+ dispose() {
218
+ if (this.renderObserver) {
219
+ this.scene.onBeforeRenderObservable.remove(this.renderObserver);
220
+ this.renderObserver = null;
221
+ }
222
+ if (this.instancedBatchSyncObserver) {
223
+ this.scene.onBeforeRenderObservable.remove(this.instancedBatchSyncObserver);
224
+ this.instancedBatchSyncObserver = null;
225
+ }
226
+ if (this.meshAddedObserver) {
227
+ this.scene.onNewMeshAddedObservable.remove(this.meshAddedObserver);
228
+ this.meshAddedObserver = null;
229
+ }
230
+ for (const [mesh, observer] of [...this.syncObservers]) {
231
+ this.scene.onBeforeRenderObservable.remove(observer);
232
+ this.syncObservers.delete(mesh);
233
+ }
234
+ for (const mesh of [...this.casters.keys()])
235
+ this.disposeCaster(mesh);
236
+ for (const batch of [...this.instancedBatches.values()])
237
+ this.disposeInstancedBatch(batch);
238
+ for (const mesh of [...this.receivers.keys()])
239
+ this.removeReceiver(mesh);
240
+ for (const material of this.materials)
241
+ material.dispose();
242
+ this.materials.clear();
243
+ this.baseMaterial = null;
244
+ this.flatMaterial?.dispose();
245
+ this.flatMaterial = null;
246
+ this.initialized = false;
247
+ }
248
+ setupStencil() {
249
+ this.scene.getEngine().setStencilBuffer(true);
250
+ this.scene.setRenderingAutoClearDepthStencil(this.options.stencil.receiverRenderingGroup, true, false, true);
251
+ this.scene.setRenderingAutoClearDepthStencil(this.options.stencil.shadowRenderingGroup, false, false, false);
252
+ }
253
+ createShadowMaterial(defines = [], alphaCutout = null) {
254
+ const materialDefines = alphaCutout
255
+ ? [...defines, 'ALPHA_CUTOUT', ...(alphaCutout.layers[1] ? ['ALPHA_CUTOUT_SECONDARY'] : [])]
256
+ : defines;
257
+ const usesBoneTexture = materialDefines.includes('BONETEXTURE');
258
+ const usesBones = materialDefines.includes('BONE');
259
+ const material = new ShaderMaterial(`fps.planarShadow.${this.materials.size}`, this.scene, { vertex: PLANAR_SHADOW_SHADER_NAME, fragment: PLANAR_SHADOW_SHADER_NAME }, {
260
+ attributes: getPlanarShadowShaderAttributes(),
261
+ uniforms: getPlanarShadowShaderUniforms(),
262
+ samplers: getPlanarShadowShaderSamplers(),
263
+ defines: materialDefines,
264
+ });
265
+ material.backFaceCulling = false;
266
+ material.disableDepthWrite = true;
267
+ material.depthFunction = Constants.LEQUAL;
268
+ material.needAlphaBlending = () => true;
269
+ material.alphaMode = Constants.ALPHA_COMBINE;
270
+ material.onBindObservable.add((mesh) => {
271
+ if (mesh instanceof AbstractMesh)
272
+ this.bindShadowProjectionUniforms(material, mesh);
273
+ if (alphaCutout) {
274
+ const primary = alphaCutout.layers[0];
275
+ material.setTexture('shadowAlphaSampler', primary.texture);
276
+ material.setFloat('u_shadowAlphaCutoff', alphaCutout.cutoff);
277
+ bindPlanarShadowAlphaCutoutLayer(material, '', primary);
278
+ const secondary = alphaCutout.layers[1];
279
+ if (secondary)
280
+ bindPlanarShadowAlphaCutoutLayer(material, '2', secondary);
281
+ }
282
+ });
283
+ if (usesBones) {
284
+ let lastBoundTexture = null;
285
+ let lastTextureWidth = 0;
286
+ material.onBindObservable.add((mesh) => {
287
+ if (!mesh?.skeleton)
288
+ return;
289
+ const skeleton = mesh.skeleton;
290
+ skeleton.prepare();
291
+ if (usesBoneTexture) {
292
+ const boneTexture = skeleton.getTransformMatrixTexture(mesh);
293
+ if (!boneTexture)
294
+ return;
295
+ const textureWidth = boneTexture.getSize().width;
296
+ if (boneTexture !== lastBoundTexture || textureWidth !== lastTextureWidth) {
297
+ material.setTexture('boneSampler', boneTexture);
298
+ material.setFloat('boneTextureWidth', textureWidth);
299
+ lastBoundTexture = boneTexture;
300
+ lastTextureWidth = textureWidth;
301
+ }
302
+ }
303
+ else {
304
+ const matrices = skeleton.getTransformMatrices(mesh);
305
+ const effect = material.getEffect();
306
+ if (matrices && effect)
307
+ effect.setMatrices('mBones', matrices);
308
+ }
309
+ });
310
+ }
311
+ if (this.options.stencil.enabled && material.stencil) {
312
+ const stencil = material.stencil;
313
+ stencil.enabled = true;
314
+ stencil.func = Constants.EQUAL;
315
+ stencil.funcRef = 1;
316
+ stencil.funcMask = 0xff;
317
+ stencil.mask = 0xff;
318
+ stencil.opStencilDepthPass = Constants.INCR;
319
+ stencil.opStencilFail = Constants.KEEP;
320
+ stencil.opDepthFail = Constants.KEEP;
321
+ stencil.backFunc = stencil.func;
322
+ stencil.backOpStencilDepthPass = stencil.opStencilDepthPass;
323
+ stencil.backOpStencilFail = stencil.opStencilFail;
324
+ stencil.backOpDepthFail = stencil.opDepthFail;
325
+ }
326
+ return material;
327
+ }
328
+ getFlatShadowMaterial() {
329
+ if (this.flatMaterial)
330
+ return this.flatMaterial;
331
+ const material = new StandardMaterial(`fps.planarShadow.flat`, this.scene);
332
+ material.backFaceCulling = false;
333
+ material.disableLighting = true;
334
+ material.disableDepthWrite = true;
335
+ material.diffuseColor = Color3.Black();
336
+ material.emissiveColor = Color3.Black();
337
+ material.alpha = clamp01(this.options.appearance.color.a);
338
+ material.alphaMode = Constants.ALPHA_COMBINE;
339
+ if (this.options.stencil.enabled && material.stencil) {
340
+ const stencil = material.stencil;
341
+ stencil.enabled = true;
342
+ stencil.func = Constants.EQUAL;
343
+ stencil.funcRef = 1;
344
+ stencil.funcMask = 0xff;
345
+ stencil.mask = 0xff;
346
+ stencil.opStencilDepthPass = Constants.INCR;
347
+ stencil.opStencilFail = Constants.KEEP;
348
+ stencil.opDepthFail = Constants.KEEP;
349
+ stencil.backFunc = stencil.func;
350
+ stencil.backOpStencilDepthPass = stencil.opStencilDepthPass;
351
+ stencil.backOpStencilFail = stencil.opStencilFail;
352
+ stencil.backOpDepthFail = stencil.opDepthFail;
353
+ }
354
+ this.flatMaterial = material;
355
+ return material;
356
+ }
357
+ updateUniforms() {
358
+ if (this.materials.size === 0)
359
+ return;
360
+ const planeNormal = new Vector3(this.options.plane.normal.x, this.options.plane.normal.y, this.options.plane.normal.z).normalize();
361
+ const planeHeight = this.resolvePlaneHeight(planeNormal);
362
+ this.resolvedPlaneNormal = planeNormal;
363
+ this.resolvedPlaneHeight = planeHeight;
364
+ const lightDir = this.resolveShadowDirection();
365
+ const shadowColor = new Color4(clamp01(this.options.appearance.color.r), clamp01(this.options.appearance.color.g), clamp01(this.options.appearance.color.b), clamp01(this.options.appearance.color.a));
366
+ for (const material of this.materials) {
367
+ material.setVector3('u_lightDir', lightDir);
368
+ material.setVector3('u_planeNormal', planeNormal);
369
+ material.setVector3('u_shadowCenter', new Vector3(0, planeHeight, 0));
370
+ material.setFloat('u_planeHeight', planeHeight);
371
+ material.setFloat('u_planeBias', this.options.plane.bias);
372
+ material.setFloat('u_footprintScale', this.options.projection.footprintScale);
373
+ material.setColor4('u_shadowColor', shadowColor);
374
+ }
375
+ if (this.flatMaterial) {
376
+ const color = new Color3(shadowColor.r, shadowColor.g, shadowColor.b);
377
+ this.flatMaterial.diffuseColor = color;
378
+ this.flatMaterial.emissiveColor = color;
379
+ this.flatMaterial.alpha = shadowColor.a;
380
+ }
381
+ }
382
+ bindShadowProjectionUniforms(material, mesh) {
383
+ mesh.computeWorldMatrix(true);
384
+ const center = mesh.getBoundingInfo()?.boundingBox.centerWorld.clone() ?? Vector3.Zero();
385
+ const offset = Vector3.Dot(this.resolvedPlaneNormal, center) - this.resolvedPlaneHeight;
386
+ const projectedCenter = center.subtract(this.resolvedPlaneNormal.scale(offset));
387
+ material.setVector3('u_shadowCenter', projectedCenter);
388
+ }
389
+ requiresPerShadowMaterial() {
390
+ return Math.abs(this.options.projection.footprintScale - 1) > 1e-6;
391
+ }
392
+ resolvePlaneHeight(planeNormal) {
393
+ const fallbackHeight = this.options.plane.height;
394
+ if (this.options.plane.heightMode !== 'receiver')
395
+ return fallbackHeight;
396
+ let totalHeight = 0;
397
+ let receiverCount = 0;
398
+ for (const mesh of this.receivers.keys()) {
399
+ if (isDisposedNode(mesh))
400
+ continue;
401
+ mesh.computeWorldMatrix(true);
402
+ const vectorsWorld = mesh.getBoundingInfo()?.boundingBox?.vectorsWorld;
403
+ if (!vectorsWorld || vectorsWorld.length === 0)
404
+ continue;
405
+ let receiverSurfaceHeight = -Infinity;
406
+ for (const point of vectorsWorld) {
407
+ const height = Vector3.Dot(planeNormal, point);
408
+ if (Number.isFinite(height))
409
+ receiverSurfaceHeight = Math.max(receiverSurfaceHeight, height);
410
+ }
411
+ if (!Number.isFinite(receiverSurfaceHeight))
412
+ continue;
413
+ totalHeight += receiverSurfaceHeight;
414
+ receiverCount += 1;
415
+ }
416
+ return receiverCount > 0 ? totalHeight / receiverCount : fallbackHeight;
417
+ }
418
+ resolveShadowDirection() {
419
+ const direction = this.directionalLight.direction;
420
+ if (direction?.lengthSquared?.() > 1e-6)
421
+ return direction.clone().normalize();
422
+ return DEFAULT_SHADOW_DIRECTION.clone();
423
+ }
424
+ refreshReceivers() {
425
+ if (!this.options.stencil.enabled)
426
+ return;
427
+ for (const [mesh, info] of [...this.receivers]) {
428
+ if (info.manual) {
429
+ if (this.hasDisabledReceiver(mesh) || this.matchesExcludePattern(mesh) || this.hasExcludedAncestor(mesh)) {
430
+ this.removeReceiver(mesh);
431
+ }
432
+ continue;
433
+ }
434
+ if (!this.matchesReceiver(mesh))
435
+ this.removeReceiver(mesh);
436
+ }
437
+ for (const mesh of this.scene.meshes) {
438
+ if (this.matchesReceiver(mesh))
439
+ this.registerReceiver(mesh, false);
440
+ }
441
+ }
442
+ pruneDisposedCasters() {
443
+ for (const [root, info] of [...this.casters]) {
444
+ if (isDisposedNode(root) || isDisposedNode(info.shadow)) {
445
+ this.disposeCaster(root);
446
+ }
447
+ }
448
+ }
449
+ disposeCaster(root) {
450
+ const info = this.casters.get(root);
451
+ if (!info)
452
+ return;
453
+ const observer = this.syncObservers.get(root);
454
+ if (observer) {
455
+ this.scene.onBeforeRenderObservable.remove(observer);
456
+ this.syncObservers.delete(root);
457
+ }
458
+ this.removeInstancedEntriesForRoot(root);
459
+ if (info.independentProjectedMeshCount > 0 || info.convexHullMeshCount > 0) {
460
+ for (const shadowMesh of info.shadow.getChildMeshes(false)) {
461
+ if (shadowMesh instanceof Mesh
462
+ && shadowMesh.material
463
+ && shadowMesh.material !== this.baseMaterial
464
+ && shadowMesh.material !== this.flatMaterial) {
465
+ this.materials.delete(shadowMesh.material);
466
+ shadowMesh.material.dispose();
467
+ }
468
+ }
469
+ info.shadow.dispose();
470
+ }
471
+ this.casters.delete(root);
472
+ }
473
+ removeInstancedEntriesForRoot(root) {
474
+ const entries = this.instancedEntriesByRoot.get(root);
475
+ if (!entries || entries.length === 0)
476
+ return;
477
+ const entriesByBatch = new Map();
478
+ for (const entry of entries) {
479
+ const batchEntries = entriesByBatch.get(entry.batchKey) ?? new Set();
480
+ batchEntries.add(entry);
481
+ entriesByBatch.set(entry.batchKey, batchEntries);
482
+ }
483
+ for (const [batchKey, batchEntries] of entriesByBatch) {
484
+ const batch = this.instancedBatches.get(batchKey);
485
+ if (!batch)
486
+ continue;
487
+ batch.entries = batch.entries.filter(candidate => !batchEntries.has(candidate));
488
+ batch.entries.forEach((candidate, index) => {
489
+ candidate.instanceIndex = index;
490
+ });
491
+ if (batch.entries.length === 0) {
492
+ this.disposeInstancedBatch(batch);
493
+ }
494
+ else {
495
+ this.markInstancedBatchBuffersDirty(batch);
496
+ }
497
+ }
498
+ this.instancedEntriesByRoot.delete(root);
499
+ }
500
+ disposeInstancedBatch(batch) {
501
+ this.instancedBatches.delete(batch.key);
502
+ batch.shadow.thinInstanceSetBuffer('shadowVisibility', null);
503
+ batch.shadow.thinInstanceSetBuffer('matrix', null);
504
+ if (batch.material !== this.baseMaterial) {
505
+ this.materials.delete(batch.material);
506
+ batch.material.dispose();
507
+ }
508
+ batch.shadow.dispose();
509
+ }
510
+ rebuildCasters() {
511
+ const roots = [...this.casters.keys()].filter((root) => !isDisposedNode(root));
512
+ for (const root of roots)
513
+ this.disposeCaster(root);
514
+ for (const root of roots)
515
+ this.addCaster(root);
516
+ }
517
+ tryAutoAddCaster(mesh) {
518
+ if (!this.initialized || this.casters.has(mesh))
519
+ return;
520
+ if (!this.options.casters.autoDetectAll && !this.matchesCasterPattern(mesh))
521
+ return;
522
+ if (!this.isValidShadowSource(mesh))
523
+ return;
524
+ const root = this.findCasterRoot(mesh);
525
+ if (this.isAlreadyCoveredByCaster(root))
526
+ return;
527
+ const shadow = this.createShadowForNode(root);
528
+ if (shadow)
529
+ this.casters.set(shadow.source, shadow);
530
+ }
531
+ ensureMeshObserver() {
532
+ if (this.meshAddedObserver)
533
+ return;
534
+ this.meshAddedObserver = this.scene.onNewMeshAddedObservable.add((mesh) => {
535
+ queueMicrotask(() => {
536
+ if (!this.initialized || mesh.isDisposed())
537
+ return;
538
+ if (this.matchesReceiver(mesh))
539
+ this.addReceiver(mesh);
540
+ this.tryAutoAddCaster(mesh);
541
+ });
542
+ });
543
+ }
544
+ createShadowForNode(root) {
545
+ const validMeshes = collectNodeMeshes(root).filter((mesh) => this.isValidShadowSource(mesh));
546
+ if (validMeshes.length === 0)
547
+ return null;
548
+ let shadowRoot = null;
549
+ const ensureShadowRoot = () => {
550
+ shadowRoot ??= new TransformNode(`${root.name}_planarShadowRoot`, this.scene);
551
+ return shadowRoot;
552
+ };
553
+ let hasSkeleton = false;
554
+ const pairs = [];
555
+ const instancedEntries = [];
556
+ const fallbackReasons = [];
557
+ const instancingFallbackReasons = [];
558
+ let sharedSkeleton = null;
559
+ let sharedMaterial = null;
560
+ for (const mesh of validMeshes) {
561
+ if (!(mesh instanceof Mesh) && !(mesh instanceof InstancedMesh))
562
+ continue;
563
+ const geometrySource = mesh instanceof InstancedMesh ? mesh.sourceMesh : mesh;
564
+ const strategy = this.options.projection.strategy;
565
+ const instancingEligibility = this.resolveInstancingEligibility(mesh);
566
+ if (instancingEligibility.eligible) {
567
+ const instanced = this.addInstancedShadowMesh(root, mesh, instancingEligibility);
568
+ if (instanced) {
569
+ instancedEntries.push(instanced);
570
+ continue;
571
+ }
572
+ instancingFallbackReasons.push(`${mesh.name}: instanced projected batch unavailable`);
573
+ }
574
+ else {
575
+ instancingFallbackReasons.push(`${mesh.name}: ${instancingEligibility.reason}`);
576
+ }
577
+ if (strategy !== 'convex-hull') {
578
+ const alphaCutout = instancingEligibility.alphaCutout;
579
+ const material = this.resolveShadowMaterial(geometrySource, sharedSkeleton, sharedMaterial, alphaCutout);
580
+ if (geometrySource.skeleton) {
581
+ hasSkeleton = true;
582
+ sharedSkeleton = geometrySource.skeleton;
583
+ if (!alphaCutout)
584
+ sharedMaterial = material;
585
+ }
586
+ const shadowMesh = material ? this.createProjectedShadowMesh(mesh, material) : null;
587
+ if (shadowMesh) {
588
+ const alphaCutoutLayers = alphaCutout?.layers.filter((layer) => !!layer) ?? [];
589
+ shadowMesh.parent = ensureShadowRoot();
590
+ pairs.push({
591
+ mode: 'projected-mesh',
592
+ source: mesh,
593
+ shadow: shadowMesh,
594
+ alphaCutout: !!alphaCutout,
595
+ alphaCutoutChannels: alphaCutoutLayers.map((layer) => layer.channel),
596
+ alphaCutoutUvSets: alphaCutoutLayers.map((layer) => layer.coordinatesIndex),
597
+ alphaCutoutLevels: alphaCutoutLayers.map((layer) => layer.level),
598
+ alphaCutoutLuminanceWeights: alphaCutoutLayers.map((layer) => layer.luminanceWeights),
599
+ });
600
+ continue;
601
+ }
602
+ fallbackReasons.push(`${mesh.name}: projected mesh clone unavailable`);
603
+ if (strategy === 'projected-mesh')
604
+ continue;
605
+ }
606
+ const shadowMesh = this.createHullShadowMesh(mesh);
607
+ if (shadowMesh) {
608
+ shadowMesh.parent = ensureShadowRoot();
609
+ pairs.push({
610
+ mode: 'flat-hull',
611
+ source: mesh,
612
+ shadow: shadowMesh,
613
+ alphaCutout: false,
614
+ alphaCutoutChannels: [],
615
+ alphaCutoutUvSets: [],
616
+ alphaCutoutLevels: [],
617
+ alphaCutoutLuminanceWeights: [],
618
+ });
619
+ }
620
+ else {
621
+ fallbackReasons.push(`${mesh.name}: convex hull geometry unavailable`);
622
+ }
623
+ }
624
+ if (pairs.length === 0 && instancedEntries.length === 0) {
625
+ this.removeInstancedEntriesForRoot(root);
626
+ return null;
627
+ }
628
+ if (pairs.length > 0 && shadowRoot) {
629
+ const sync = () => {
630
+ if (!shadowRoot || shadowRoot.isDisposed())
631
+ return;
632
+ for (const pair of pairs) {
633
+ if (pair.source.isDisposed() || pair.shadow.isDisposed())
634
+ continue;
635
+ if (pair.mode === 'flat-hull') {
636
+ this.updateHullShadowMesh(pair.source, pair.shadow);
637
+ }
638
+ else {
639
+ pair.shadow.setEnabled(pair.source.isEnabled());
640
+ pair.shadow.isVisible = pair.source.isVisible;
641
+ pair.source.computeWorldMatrix(true).decompose(pair.shadow.scaling, pair.shadow.rotationQuaternion, pair.shadow.position);
642
+ }
643
+ }
644
+ };
645
+ sync();
646
+ const observer = this.scene.onBeforeRenderObservable.add(sync);
647
+ this.syncObservers.set(root, observer);
648
+ }
649
+ const instancedBatchKeys = uniqueStrings(instancedEntries.map((entry) => entry.batchKey));
650
+ const instancedBatchNames = uniqueStrings(instancedBatchKeys.map((key) => this.instancedBatches.get(key)?.shadow.name ?? ''));
651
+ const instancedAlphaCutoutLayers = instancedEntries.flatMap((entry) => {
652
+ const batch = this.instancedBatches.get(entry.batchKey);
653
+ return batch?.alphaCutout?.layers.filter((layer) => !!layer) ?? [];
654
+ });
655
+ const instancedAlphaCutoutMeshCount = instancedEntries.filter((entry) => {
656
+ const batch = this.instancedBatches.get(entry.batchKey);
657
+ return !!batch?.alphaCutout;
658
+ }).length;
659
+ const independentProjectedMeshCount = pairs.filter((pair) => pair.mode === 'projected-mesh').length;
660
+ const visibleInstanceCount = instancedEntries.filter((entry) => readPlanarShadowEffectiveVisibility(entry.source)).length;
661
+ return {
662
+ source: root,
663
+ shadow: shadowRoot ?? this.instancedBatches.get(instancedEntries[0]?.batchKey)?.shadow ?? root,
664
+ sourceMeshNames: uniqueStrings([
665
+ ...pairs.map((pair) => pair.source.name),
666
+ ...instancedEntries.map((entry) => entry.source.name),
667
+ ]),
668
+ sourceMeshUniqueIds: uniqueNumbers([
669
+ ...pairs.map((pair) => pair.source.uniqueId),
670
+ ...instancedEntries.map((entry) => entry.source.uniqueId),
671
+ ]),
672
+ hasSkeleton,
673
+ strategy: summarizePlanarShadowStrategy(pairs, instancedEntries.length),
674
+ projectedMeshCount: independentProjectedMeshCount + instancedBatchKeys.length,
675
+ convexHullMeshCount: pairs.filter((pair) => pair.mode === 'flat-hull').length,
676
+ alphaCutoutMeshCount: pairs.filter((pair) => pair.alphaCutout).length + instancedAlphaCutoutMeshCount,
677
+ instancedCasterCount: instancedEntries.length,
678
+ batchMeshCount: instancedBatchKeys.length,
679
+ instancedBatchIds: instancedBatchKeys,
680
+ instancedBatchNames,
681
+ independentProjectedMeshCount,
682
+ visibleInstanceCount,
683
+ alphaCutoutChannels: [
684
+ ...pairs.flatMap((pair) => pair.alphaCutoutChannels),
685
+ ...instancedAlphaCutoutLayers.map((layer) => layer.channel),
686
+ ],
687
+ alphaCutoutUvSets: [
688
+ ...pairs.flatMap((pair) => pair.alphaCutoutUvSets),
689
+ ...instancedAlphaCutoutLayers.map((layer) => layer.coordinatesIndex),
690
+ ],
691
+ alphaCutoutLevels: [
692
+ ...pairs.flatMap((pair) => pair.alphaCutoutLevels),
693
+ ...instancedAlphaCutoutLayers.map((layer) => layer.level),
694
+ ],
695
+ alphaCutoutLuminanceWeights: [
696
+ ...pairs.flatMap((pair) => pair.alphaCutoutLuminanceWeights),
697
+ ...instancedAlphaCutoutLayers.map((layer) => layer.luminanceWeights),
698
+ ],
699
+ fallbackReasons,
700
+ instancingFallbackReasons,
701
+ };
702
+ }
703
+ resolveInstancingEligibility(source) {
704
+ const geometrySource = source instanceof InstancedMesh ? source.sourceMesh : source;
705
+ const alphaCutout = resolvePlanarShadowAlphaCutout(geometrySource);
706
+ if (this.options.projection.strategy !== 'auto') {
707
+ return {
708
+ eligible: false,
709
+ reason: `projection strategy ${this.options.projection.strategy} bypasses instanced backend`,
710
+ alphaCutout,
711
+ };
712
+ }
713
+ if (this.requiresPerShadowMaterial()) {
714
+ return {
715
+ eligible: false,
716
+ reason: 'footprintScale requires per-caster projected material',
717
+ alphaCutout,
718
+ };
719
+ }
720
+ if (!geometrySource.geometry) {
721
+ return {
722
+ eligible: false,
723
+ reason: 'geometry unavailable',
724
+ alphaCutout,
725
+ };
726
+ }
727
+ if (geometrySource.skeleton) {
728
+ return {
729
+ eligible: false,
730
+ reason: 'skinned caster requires independent projected mesh',
731
+ alphaCutout,
732
+ };
733
+ }
734
+ if (geometrySource.morphTargetManager) {
735
+ return {
736
+ eligible: false,
737
+ reason: 'morph target caster requires independent projected mesh',
738
+ alphaCutout,
739
+ };
740
+ }
741
+ if (geometrySource.hasThinInstances) {
742
+ return {
743
+ eligible: false,
744
+ reason: 'source mesh thin instances require independent projected mesh',
745
+ alphaCutout,
746
+ };
747
+ }
748
+ const alphaSignature = createPlanarShadowInstancedAlphaCutoutSignature(alphaCutout);
749
+ if (!alphaSignature.signature) {
750
+ return {
751
+ eligible: false,
752
+ reason: alphaSignature.reason,
753
+ alphaCutout,
754
+ };
755
+ }
756
+ const geometryId = readPlanarShadowGeometryIdentity(geometrySource);
757
+ return {
758
+ eligible: true,
759
+ batchKey: [
760
+ 'projected',
761
+ `geometry:${geometryId}`,
762
+ `alpha:${alphaSignature.signature}`,
763
+ `shadowGroup:${this.options.stencil.shadowRenderingGroup}`,
764
+ `stencil:${this.options.stencil.enabled ? 1 : 0}`,
765
+ ].join('|'),
766
+ geometrySource,
767
+ alphaCutout,
768
+ alphaCutoutSignature: alphaSignature.signature,
769
+ };
770
+ }
771
+ addInstancedShadowMesh(root, source, eligibility) {
772
+ const batch = this.getOrCreateInstancedBatch(eligibility);
773
+ if (!batch)
774
+ return null;
775
+ const entry = {
776
+ root,
777
+ source,
778
+ geometrySource: eligibility.geometrySource,
779
+ instanceIndex: batch.entries.length,
780
+ batchKey: eligibility.batchKey,
781
+ };
782
+ batch.entries.push(entry);
783
+ const entries = this.instancedEntriesByRoot.get(root) ?? [];
784
+ entries.push(entry);
785
+ this.instancedEntriesByRoot.set(root, entries);
786
+ this.markInstancedBatchBuffersDirty(batch);
787
+ return entry;
788
+ }
789
+ getOrCreateInstancedBatch(eligibility) {
790
+ const existing = this.instancedBatches.get(eligibility.batchKey);
791
+ if (existing)
792
+ return existing;
793
+ const material = this.resolveShadowMaterial(eligibility.geometrySource, null, null, eligibility.alphaCutout);
794
+ if (!material)
795
+ return null;
796
+ const shadow = this.createInstancedProjectedShadowMesh(eligibility.geometrySource, material);
797
+ if (!shadow) {
798
+ if (material !== this.baseMaterial) {
799
+ this.materials.delete(material);
800
+ material.dispose();
801
+ }
802
+ return null;
803
+ }
804
+ const batch = {
805
+ key: eligibility.batchKey,
806
+ shadow,
807
+ material,
808
+ geometrySource: eligibility.geometrySource,
809
+ alphaCutout: eligibility.alphaCutout,
810
+ entries: [],
811
+ matrixData: new Float32Array(0),
812
+ visibilityData: new Float32Array(0),
813
+ buffersDirty: true,
814
+ };
815
+ this.instancedBatches.set(batch.key, batch);
816
+ return batch;
817
+ }
818
+ createInstancedProjectedShadowMesh(geometrySource, material) {
819
+ const shadowMesh = geometrySource.clone(`${geometrySource.name}_planarShadowBatch`, null, true, false);
820
+ if (!shadowMesh)
821
+ return null;
822
+ shadowMesh.parent = null;
823
+ shadowMesh.position.setAll(0);
824
+ shadowMesh.rotation.setAll(0);
825
+ shadowMesh.scaling.setAll(1);
826
+ shadowMesh.rotationQuaternion = null;
827
+ shadowMesh.material = material;
828
+ shadowMesh.isPickable = false;
829
+ shadowMesh.receiveShadows = false;
830
+ shadowMesh.renderingGroupId = this.options.stencil.shadowRenderingGroup;
831
+ shadowMesh.alphaIndex = PLANAR_SHADOW_ALPHA_INDEX;
832
+ shadowMesh.renderOutline = false;
833
+ shadowMesh.renderOverlay = false;
834
+ shadowMesh.alwaysSelectAsActiveMesh = true;
835
+ shadowMesh.metadata = {
836
+ ...(shadowMesh.metadata && typeof shadowMesh.metadata === 'object' ? shadowMesh.metadata : {}),
837
+ disablePlanarShadow: true,
838
+ planarShadowInternal: true,
839
+ planarShadowInstancedBatch: true,
840
+ };
841
+ shadowMesh.thinInstanceSetBuffer('matrix', new Float32Array(0), 16, false);
842
+ shadowMesh.thinInstanceSetBuffer('shadowVisibility', new Float32Array(0), 1, false);
843
+ return shadowMesh;
844
+ }
845
+ rebuildInstancedBatchBuffers(batch) {
846
+ const instanceCount = batch.entries.length;
847
+ batch.matrixData = new Float32Array(instanceCount * 16);
848
+ batch.visibilityData = new Float32Array(instanceCount);
849
+ this.writeInstancedBatchData(batch);
850
+ batch.shadow.thinInstanceSetBuffer('matrix', batch.matrixData, 16, false);
851
+ batch.shadow.thinInstanceSetBuffer('shadowVisibility', batch.visibilityData, 1, false);
852
+ batch.shadow.thinInstanceCount = instanceCount;
853
+ batch.buffersDirty = false;
854
+ batch.shadow.isVisible = instanceCount > 0;
855
+ }
856
+ syncInstancedBatches() {
857
+ for (const batch of this.instancedBatches.values()) {
858
+ if (this.shouldRebuildInstancedBatchBuffers(batch)) {
859
+ this.rebuildInstancedBatchBuffers(batch);
860
+ }
861
+ else {
862
+ this.writeInstancedBatchData(batch);
863
+ batch.shadow.thinInstanceBufferUpdated('matrix');
864
+ batch.shadow.thinInstanceBufferUpdated('shadowVisibility');
865
+ }
866
+ batch.shadow.isVisible = batch.entries.length > 0 && batch.visibilityData.some(value => value >= 0.5);
867
+ }
868
+ }
869
+ markInstancedBatchBuffersDirty(batch) {
870
+ batch.buffersDirty = true;
871
+ batch.shadow.thinInstanceCount = batch.entries.length;
872
+ }
873
+ shouldRebuildInstancedBatchBuffers(batch) {
874
+ return batch.buffersDirty
875
+ || batch.matrixData.length !== batch.entries.length * 16
876
+ || batch.visibilityData.length !== batch.entries.length;
877
+ }
878
+ writeInstancedBatchData(batch) {
879
+ batch.entries.forEach((entry, index) => {
880
+ entry.instanceIndex = index;
881
+ if (entry.source.isDisposed()) {
882
+ Matrix.Identity().copyToArray(batch.matrixData, index * 16);
883
+ batch.visibilityData[index] = 0;
884
+ return;
885
+ }
886
+ entry.source.computeWorldMatrix(true);
887
+ entry.source.getWorldMatrix().copyToArray(batch.matrixData, index * 16);
888
+ batch.visibilityData[index] = readPlanarShadowEffectiveVisibility(entry.source) ? 1 : 0;
889
+ });
890
+ for (const [root, info] of this.casters) {
891
+ const entries = this.instancedEntriesByRoot.get(root);
892
+ if (!entries?.some((entry) => entry.batchKey === batch.key))
893
+ continue;
894
+ info.visibleInstanceCount = entries.filter((entry) => readPlanarShadowEffectiveVisibility(entry.source)).length;
895
+ }
896
+ }
897
+ resolveSkeletonShadowMaterial(geometrySource, sharedSkeleton, sharedMaterial) {
898
+ if (geometrySource.skeleton === sharedSkeleton && sharedMaterial)
899
+ return sharedMaterial;
900
+ const numBones = geometrySource.skeleton?.bones.length ?? 0;
901
+ const useTexture = !!this.scene.getEngine().getCaps().textureFloat && numBones > 4;
902
+ const hasExtra = geometrySource.isVerticesDataPresent('matricesIndicesExtra');
903
+ const material = this.createShadowMaterial(getPlanarShadowSkeletonDefines(numBones, useTexture, hasExtra));
904
+ this.materials.add(material);
905
+ return material;
906
+ }
907
+ resolveShadowMaterial(geometrySource, sharedSkeleton, sharedMaterial, alphaCutout = null) {
908
+ if (alphaCutout) {
909
+ const material = geometrySource.skeleton
910
+ ? this.createShadowMaterial(getPlanarShadowSkeletonDefines(geometrySource.skeleton?.bones.length ?? 0, !!this.scene.getEngine().getCaps().textureFloat && (geometrySource.skeleton?.bones.length ?? 0) > 4, geometrySource.isVerticesDataPresent('matricesIndicesExtra')), alphaCutout)
911
+ : this.createShadowMaterial([], alphaCutout);
912
+ this.materials.add(material);
913
+ return material;
914
+ }
915
+ if (this.requiresPerShadowMaterial()) {
916
+ const material = geometrySource.skeleton
917
+ ? this.createShadowMaterial(getPlanarShadowSkeletonDefines(geometrySource.skeleton?.bones.length ?? 0, !!this.scene.getEngine().getCaps().textureFloat && (geometrySource.skeleton?.bones.length ?? 0) > 4, geometrySource.isVerticesDataPresent('matricesIndicesExtra')))
918
+ : this.createShadowMaterial();
919
+ this.materials.add(material);
920
+ return material;
921
+ }
922
+ return geometrySource.skeleton
923
+ ? this.resolveSkeletonShadowMaterial(geometrySource, sharedSkeleton, sharedMaterial)
924
+ : this.baseMaterial;
925
+ }
926
+ createProjectedShadowMesh(source, material) {
927
+ const geometrySource = source instanceof InstancedMesh ? source.sourceMesh : source;
928
+ const shadowMesh = geometrySource.clone(`${source.name}_planarShadow`, null, true, false);
929
+ if (!shadowMesh)
930
+ return null;
931
+ if (geometrySource.skeleton) {
932
+ shadowMesh.skeleton = geometrySource.skeleton;
933
+ geometrySource.skeleton.prepare();
934
+ }
935
+ shadowMesh.material = material;
936
+ shadowMesh.isPickable = false;
937
+ shadowMesh.receiveShadows = false;
938
+ shadowMesh.renderingGroupId = this.options.stencil.shadowRenderingGroup;
939
+ shadowMesh.alphaIndex = PLANAR_SHADOW_ALPHA_INDEX;
940
+ shadowMesh.renderOutline = false;
941
+ shadowMesh.renderOverlay = false;
942
+ shadowMesh.metadata = {
943
+ ...(shadowMesh.metadata && typeof shadowMesh.metadata === 'object' ? shadowMesh.metadata : {}),
944
+ disablePlanarShadow: true,
945
+ planarShadowInternal: true,
946
+ };
947
+ if (!shadowMesh.rotationQuaternion)
948
+ shadowMesh.rotationQuaternion = shadowMesh.rotation.toQuaternion();
949
+ return shadowMesh;
950
+ }
951
+ createHullShadowMesh(source) {
952
+ const shadowMesh = new Mesh(`${source.name}_planarShadow`, this.scene);
953
+ shadowMesh.material = this.getFlatShadowMaterial();
954
+ shadowMesh.isPickable = false;
955
+ shadowMesh.receiveShadows = false;
956
+ shadowMesh.renderingGroupId = this.options.stencil.shadowRenderingGroup;
957
+ shadowMesh.alphaIndex = PLANAR_SHADOW_ALPHA_INDEX;
958
+ shadowMesh.renderOutline = false;
959
+ shadowMesh.renderOverlay = false;
960
+ shadowMesh.metadata = {
961
+ ...(shadowMesh.metadata && typeof shadowMesh.metadata === 'object' ? shadowMesh.metadata : {}),
962
+ disablePlanarShadow: true,
963
+ planarShadowInternal: true,
964
+ };
965
+ this.updateHullShadowMesh(source, shadowMesh);
966
+ return shadowMesh;
967
+ }
968
+ updateHullShadowMesh(source, shadowMesh) {
969
+ const geometry = buildPlanarShadowHullGeometry(source, {
970
+ lightDir: this.resolveShadowDirection(),
971
+ planeNormal: this.resolvedPlaneNormal,
972
+ planeHeight: this.resolvedPlaneHeight,
973
+ planeBias: this.options.plane.bias,
974
+ footprintScale: this.options.projection.footprintScale,
975
+ });
976
+ if (!geometry) {
977
+ shadowMesh.isVisible = false;
978
+ return;
979
+ }
980
+ const vertexData = new VertexData();
981
+ vertexData.positions = geometry.positions;
982
+ vertexData.indices = geometry.indices;
983
+ vertexData.normals = geometry.normals;
984
+ vertexData.applyToMesh(shadowMesh, true);
985
+ shadowMesh.isVisible = true;
986
+ }
987
+ isValidShadowSource(mesh) {
988
+ return this.getInvalidShadowSourceReason(mesh) === null;
989
+ }
990
+ getInvalidShadowSourceReason(mesh) {
991
+ if (isDisposedNode(mesh))
992
+ return 'source disposed';
993
+ if (!(mesh instanceof Mesh) && !(mesh instanceof InstancedMesh))
994
+ return 'source is not a mesh';
995
+ if (mesh instanceof Mesh && !mesh.geometry)
996
+ return 'geometry unavailable';
997
+ if (mesh instanceof InstancedMesh && !mesh.sourceMesh.geometry)
998
+ return 'instance source geometry unavailable';
999
+ if (hasDisablePlanarShadowMetadata(mesh))
1000
+ return 'planar shadow disabled by metadata';
1001
+ if (this.matchesExcludePattern(mesh))
1002
+ return 'source matches exclude pattern';
1003
+ if (this.hasExcludedAncestor(mesh))
1004
+ return 'ancestor matches exclude or disabled pattern';
1005
+ if (this.matchesReceiverPattern(mesh))
1006
+ return 'source matches receiver pattern';
1007
+ mesh.computeWorldMatrix(true);
1008
+ const size = mesh.getBoundingInfo()?.boundingBox.extendSizeWorld;
1009
+ if (!size)
1010
+ return null;
1011
+ const volume = size.x * size.y * size.z;
1012
+ if (volume >= this.options.casters.minVolume)
1013
+ return null;
1014
+ const maxProjectedArea = Math.max(size.x * size.y, size.x * size.z, size.y * size.z);
1015
+ return maxProjectedArea >= this.options.casters.minVolume
1016
+ ? null
1017
+ : `bounds below minVolume volume=${volume} maxProjectedArea=${maxProjectedArea}`;
1018
+ }
1019
+ findCasterRoot(mesh) {
1020
+ let topmost = mesh;
1021
+ let current = mesh.parent;
1022
+ while (current) {
1023
+ if (this.matchesRootBoundaryPattern(current) || hasDisablePlanarShadowMetadata(current))
1024
+ break;
1025
+ if (!(current instanceof AbstractMesh)) {
1026
+ current = current.parent;
1027
+ continue;
1028
+ }
1029
+ if (this.matchesExcludePattern(current))
1030
+ break;
1031
+ topmost = current;
1032
+ current = current.parent;
1033
+ }
1034
+ return topmost;
1035
+ }
1036
+ matchesCasterPattern(mesh) {
1037
+ return this.casterPatterns.some((pattern) => this.nodeOrAncestorNameIncludes(mesh, pattern));
1038
+ }
1039
+ matchesReceiver(mesh) {
1040
+ return this.options.stencil.enabled
1041
+ && !hasDisablePlanarShadowMetadata(mesh)
1042
+ && !this.hasDisabledReceiver(mesh)
1043
+ && !this.matchesExcludePattern(mesh)
1044
+ && !this.hasExcludedAncestor(mesh)
1045
+ && this.matchesReceiverPattern(mesh);
1046
+ }
1047
+ matchesReceiverPattern(mesh) {
1048
+ return this.options.receivers.patterns.some((pattern) => this.nodeOrAncestorNameIncludes(mesh, pattern));
1049
+ }
1050
+ matchesExcludePattern(node) {
1051
+ const name = node.name?.toLowerCase() ?? '';
1052
+ return this.options.casters.excludePatterns.some((pattern) => pattern && name.includes(pattern.toLowerCase()));
1053
+ }
1054
+ matchesRootBoundaryPattern(node) {
1055
+ const name = node.name?.toLowerCase() ?? '';
1056
+ return this.options.casters.rootBoundaryPatterns.some((pattern) => pattern && name.includes(pattern.toLowerCase()));
1057
+ }
1058
+ hasExcludedAncestor(mesh) {
1059
+ let current = mesh.parent;
1060
+ while (current) {
1061
+ if (this.matchesRootBoundaryPattern(current))
1062
+ return false;
1063
+ if (hasDisablePlanarShadowMetadata(current) || this.matchesExcludePattern(current))
1064
+ return true;
1065
+ current = current.parent;
1066
+ }
1067
+ return false;
1068
+ }
1069
+ hasDisabledReceiver(mesh) {
1070
+ return hasDisabledPlanarShadowReceiver(mesh, node => this.matchesRootBoundaryPattern(node));
1071
+ }
1072
+ isAlreadyCoveredByCaster(node) {
1073
+ if (this.casters.has(node))
1074
+ return true;
1075
+ let current = node.parent;
1076
+ while (current) {
1077
+ if (current instanceof AbstractMesh && this.casters.has(current))
1078
+ return true;
1079
+ current = current.parent;
1080
+ }
1081
+ for (const descendant of node.getChildMeshes(false)) {
1082
+ if (this.casters.has(descendant))
1083
+ return true;
1084
+ }
1085
+ return false;
1086
+ }
1087
+ nodeOrAncestorNameIncludes(mesh, pattern) {
1088
+ const normalized = pattern.toLowerCase();
1089
+ if (!normalized)
1090
+ return false;
1091
+ return this.readNodeAndAncestorNames(mesh).some(name => name.includes(normalized));
1092
+ }
1093
+ readNodeAndAncestorNames(mesh) {
1094
+ const cache = this.patternNameCache;
1095
+ const cached = cache?.get(mesh);
1096
+ if (cached)
1097
+ return cached;
1098
+ const names = [];
1099
+ let current = mesh;
1100
+ const seen = new Set();
1101
+ while (current && typeof current === 'object' && !seen.has(current)) {
1102
+ seen.add(current);
1103
+ const name = current.name;
1104
+ if (typeof name === 'string')
1105
+ names.push(name.toLowerCase());
1106
+ current = current.parent;
1107
+ }
1108
+ cache?.set(mesh, names);
1109
+ return names;
1110
+ }
1111
+ }
1112
+ function buildPlanarShadowHullGeometry(source, options) {
1113
+ const geometrySource = source instanceof InstancedMesh ? source.sourceMesh : source;
1114
+ const positions = geometrySource.getVerticesData(VertexBuffer.PositionKind);
1115
+ if (!positions || positions.length < 9)
1116
+ return null;
1117
+ const planeNormal = options.planeNormal.clone().normalize();
1118
+ const lightDir = options.lightDir.clone().normalize();
1119
+ const denom = Vector3.Dot(planeNormal, lightDir);
1120
+ if (Math.abs(denom) < 1e-5)
1121
+ return null;
1122
+ source.computeWorldMatrix(true);
1123
+ const worldMatrix = source.getWorldMatrix();
1124
+ const projectedPoints = [];
1125
+ for (let index = 0; index < positions.length; index += 3) {
1126
+ const localPoint = new Vector3(Number(positions[index]), Number(positions[index + 1]), Number(positions[index + 2]));
1127
+ const worldPoint = Vector3.TransformCoordinates(localPoint, worldMatrix);
1128
+ const t = (options.planeHeight - Vector3.Dot(planeNormal, worldPoint)) / denom;
1129
+ if (t < -1e-4)
1130
+ continue;
1131
+ const projectedPoint = worldPoint.add(lightDir.scale(Math.max(0, t)));
1132
+ projectedPoints.push(projectedPoint);
1133
+ }
1134
+ if (projectedPoints.length < 3)
1135
+ return null;
1136
+ const scaleCenter = averageVector3(projectedPoints);
1137
+ const footprintScale = Number.isFinite(options.footprintScale) ? Math.max(0, options.footprintScale) : 1;
1138
+ const biasedPoints = projectedPoints.map((point) => {
1139
+ const scaled = footprintScale === 1
1140
+ ? point
1141
+ : scaleCenter.add(point.subtract(scaleCenter).scale(footprintScale));
1142
+ return scaled.add(planeNormal.scale(options.planeBias * 0.002));
1143
+ });
1144
+ const basis = buildPlaneBasis(planeNormal);
1145
+ const points2d = dedupeHullPoints(biasedPoints.map((point) => ({
1146
+ x: Vector3.Dot(point, basis.tangent),
1147
+ y: Vector3.Dot(point, basis.bitangent),
1148
+ point,
1149
+ })));
1150
+ if (points2d.length < 3)
1151
+ return null;
1152
+ const hull = buildConvexHull(points2d);
1153
+ if (hull.length < 3)
1154
+ return null;
1155
+ const center = averageVector3(hull.map((point) => point.point));
1156
+ const positionsOut = [center.x, center.y, center.z];
1157
+ const normalsOut = [planeNormal.x, planeNormal.y, planeNormal.z];
1158
+ for (const hullPoint of hull) {
1159
+ positionsOut.push(hullPoint.point.x, hullPoint.point.y, hullPoint.point.z);
1160
+ normalsOut.push(planeNormal.x, planeNormal.y, planeNormal.z);
1161
+ }
1162
+ const indicesOut = [];
1163
+ for (let index = 1; index <= hull.length; index += 1) {
1164
+ indicesOut.push(0, index, index === hull.length ? 1 : index + 1);
1165
+ }
1166
+ return {
1167
+ positions: positionsOut,
1168
+ indices: indicesOut,
1169
+ normals: normalsOut,
1170
+ };
1171
+ }
1172
+ function bindPlanarShadowAlphaCutoutLayer(material, suffix, layer) {
1173
+ material.setFloat(`u_shadowAlphaLevel${suffix}`, layer.level);
1174
+ material.setFloat(`u_shadowAlphaChannelMode${suffix}`, layer.channel === 'red' ? 2 : layer.channel === 'luminance' ? 1 : 0);
1175
+ material.setVector3(`u_shadowAlphaLuminanceWeights${suffix}`, layer.luminanceWeights);
1176
+ material.setFloat(`u_shadowAlphaUvSet${suffix}`, layer.coordinatesIndex);
1177
+ material.setMatrix(`u_shadowAlphaMatrix${suffix}`, layer.texture.getTextureMatrix?.() ?? Matrix.Identity());
1178
+ material.setTexture(`shadowAlphaSampler${suffix}`, layer.texture);
1179
+ }
1180
+ function createPlanarShadowInstancedAlphaCutoutSignature(alphaCutout) {
1181
+ if (!alphaCutout)
1182
+ return { signature: 'opaque', reason: '' };
1183
+ const layers = alphaCutout.layers.filter((layer) => !!layer);
1184
+ if (layers.length !== 1) {
1185
+ return {
1186
+ signature: null,
1187
+ reason: 'multi-layer alpha cutout requires independent projected mesh',
1188
+ };
1189
+ }
1190
+ const [layer] = layers;
1191
+ const textureIdentity = readPlanarShadowTextureIdentity(layer.texture);
1192
+ if (!textureIdentity) {
1193
+ return {
1194
+ signature: null,
1195
+ reason: 'alpha cutout texture identity unavailable',
1196
+ };
1197
+ }
1198
+ return {
1199
+ signature: [
1200
+ 'cutout',
1201
+ `texture:${textureIdentity}`,
1202
+ `channel:${layer.channel}`,
1203
+ `uv:${layer.coordinatesIndex}`,
1204
+ `level:${formatPlanarShadowSignatureNumber(layer.level)}`,
1205
+ `cutoff:${formatPlanarShadowSignatureNumber(alphaCutout.cutoff)}`,
1206
+ `weights:${formatPlanarShadowSignatureNumber(layer.luminanceWeights.x)},${formatPlanarShadowSignatureNumber(layer.luminanceWeights.y)},${formatPlanarShadowSignatureNumber(layer.luminanceWeights.z)}`,
1207
+ `matrix:${serializePlanarShadowTextureMatrix(layer.texture)}`,
1208
+ ].join('|'),
1209
+ reason: '',
1210
+ };
1211
+ }
1212
+ function readPlanarShadowGeometryIdentity(mesh) {
1213
+ const geometry = mesh.geometry;
1214
+ const geometryUniqueId = geometry?.uniqueId;
1215
+ if (typeof geometryUniqueId === 'number' && Number.isFinite(geometryUniqueId))
1216
+ return `id:${geometryUniqueId}`;
1217
+ if (typeof geometryUniqueId === 'string' && geometryUniqueId.length > 0)
1218
+ return `id:${geometryUniqueId}`;
1219
+ return `mesh:${mesh.uniqueId}`;
1220
+ }
1221
+ function readPlanarShadowTextureIdentity(texture) {
1222
+ const record = texture;
1223
+ if (typeof record.uniqueId === 'number' && Number.isFinite(record.uniqueId))
1224
+ return `id:${record.uniqueId}`;
1225
+ if (typeof record.uniqueId === 'string' && record.uniqueId.length > 0)
1226
+ return `id:${record.uniqueId}`;
1227
+ if (typeof record.uid === 'string' && record.uid.length > 0)
1228
+ return `uid:${record.uid}`;
1229
+ if (typeof record.url === 'string' && record.url.length > 0)
1230
+ return `url:${record.url}`;
1231
+ if (typeof record.name === 'string' && record.name.length > 0)
1232
+ return `name:${record.name}`;
1233
+ return null;
1234
+ }
1235
+ function serializePlanarShadowTextureMatrix(texture) {
1236
+ const matrix = texture.getTextureMatrix?.() ?? Matrix.Identity();
1237
+ return [...matrix.m].map(formatPlanarShadowSignatureNumber).join(',');
1238
+ }
1239
+ function formatPlanarShadowSignatureNumber(value) {
1240
+ if (!Number.isFinite(value))
1241
+ return '0';
1242
+ return String(Math.round(value * 1000000) / 1000000);
1243
+ }
1244
+ function readPlanarShadowEffectiveVisibility(node) {
1245
+ let current = node;
1246
+ while (current) {
1247
+ if (isDisposedNode(current))
1248
+ return false;
1249
+ if (current.isEnabled?.() === false)
1250
+ return false;
1251
+ if (current.isVisible === false)
1252
+ return false;
1253
+ if (typeof current.visibility === 'number' && current.visibility <= 0)
1254
+ return false;
1255
+ current = current.parent && typeof current.parent === 'object'
1256
+ ? current.parent
1257
+ : null;
1258
+ }
1259
+ return true;
1260
+ }
1261
+ function buildPlaneBasis(normal) {
1262
+ const seed = Math.abs(Vector3.Dot(normal, Vector3.Up())) > 0.95
1263
+ ? Vector3.Right()
1264
+ : Vector3.Up();
1265
+ const tangent = Vector3.Cross(seed, normal).normalize();
1266
+ const bitangent = Vector3.Cross(normal, tangent).normalize();
1267
+ return { tangent, bitangent };
1268
+ }
1269
+ function dedupeHullPoints(points) {
1270
+ const seen = new Map();
1271
+ for (const point of points) {
1272
+ const key = `${Math.round(point.x * 100000)}:${Math.round(point.y * 100000)}`;
1273
+ if (!seen.has(key))
1274
+ seen.set(key, point);
1275
+ }
1276
+ return [...seen.values()];
1277
+ }
1278
+ function buildConvexHull(points) {
1279
+ const sorted = [...points].sort((a, b) => (a.x === b.x ? a.y - b.y : a.x - b.x));
1280
+ const lower = [];
1281
+ for (const point of sorted) {
1282
+ while (lower.length >= 2 && hullCross(lower[lower.length - 2], lower[lower.length - 1], point) <= 1e-7) {
1283
+ lower.pop();
1284
+ }
1285
+ lower.push(point);
1286
+ }
1287
+ const upper = [];
1288
+ for (let index = sorted.length - 1; index >= 0; index -= 1) {
1289
+ const point = sorted[index];
1290
+ while (upper.length >= 2 && hullCross(upper[upper.length - 2], upper[upper.length - 1], point) <= 1e-7) {
1291
+ upper.pop();
1292
+ }
1293
+ upper.push(point);
1294
+ }
1295
+ lower.pop();
1296
+ upper.pop();
1297
+ return lower.concat(upper);
1298
+ }
1299
+ function hullCross(origin, a, b) {
1300
+ return (a.x - origin.x) * (b.y - origin.y) - (a.y - origin.y) * (b.x - origin.x);
1301
+ }
1302
+ function averageVector3(points) {
1303
+ const total = points.reduce((sum, point) => sum.addInPlace(point), Vector3.Zero());
1304
+ return total.scale(1 / points.length);
1305
+ }
1306
+ function resolvePlanarShadowOptions(override, base = DEFAULT_PLANAR_SHADOW_OPTIONS) {
1307
+ return {
1308
+ enabled: override.enabled ?? base.enabled,
1309
+ plane: {
1310
+ ...base.plane,
1311
+ ...override.plane,
1312
+ heightMode: readPlaneHeightMode(override.plane?.heightMode, base.plane.heightMode),
1313
+ },
1314
+ appearance: {
1315
+ color: { ...base.appearance.color, ...override.appearance?.color },
1316
+ },
1317
+ direction: { mode: 'follow-light' },
1318
+ projection: {
1319
+ footprintScale: readNonNegativeFiniteNumber(override.projection?.footprintScale, base.projection.footprintScale),
1320
+ strategy: readPlanarShadowProjectionStrategy(override.projection?.strategy, base.projection.strategy),
1321
+ },
1322
+ stencil: {
1323
+ enabled: override.stencil?.enabled ?? base.stencil.enabled,
1324
+ receiverRenderingGroup: override.stencil?.receiverRenderingGroup ?? base.stencil.receiverRenderingGroup,
1325
+ shadowRenderingGroup: override.stencil?.shadowRenderingGroup ?? base.stencil.shadowRenderingGroup,
1326
+ },
1327
+ casters: {
1328
+ autoDetectAll: override.casters?.autoDetectAll ?? base.casters.autoDetectAll,
1329
+ includePatterns: override.casters?.includePatterns ? [...override.casters.includePatterns] : [...base.casters.includePatterns],
1330
+ excludePatterns: override.casters?.excludePatterns ? [...override.casters.excludePatterns] : [...base.casters.excludePatterns],
1331
+ rootBoundaryPatterns: override.casters?.rootBoundaryPatterns
1332
+ ? [...override.casters.rootBoundaryPatterns]
1333
+ : [...base.casters.rootBoundaryPatterns],
1334
+ minVolume: override.casters?.minVolume ?? base.casters.minVolume,
1335
+ },
1336
+ receivers: {
1337
+ patterns: override.receivers?.patterns ? [...override.receivers.patterns] : [...base.receivers.patterns],
1338
+ },
1339
+ debug: override.debug ?? base.debug,
1340
+ };
1341
+ }
1342
+ function summarizePlanarShadowStrategy(pairs, instancedProjectedCount = 0) {
1343
+ const projectedCount = pairs.filter((pair) => pair.mode === 'projected-mesh').length + instancedProjectedCount;
1344
+ const hullCount = pairs.filter((pair) => pair.mode === 'flat-hull').length;
1345
+ if (projectedCount > 0 && hullCount === 0)
1346
+ return 'projected-mesh';
1347
+ if (hullCount > 0 && projectedCount === 0)
1348
+ return 'convex-hull';
1349
+ return 'auto';
1350
+ }
1351
+ function uniqueStrings(values) {
1352
+ return [...new Set(values.filter(value => value.length > 0))];
1353
+ }
1354
+ function uniqueNumbers(values) {
1355
+ return [...new Set(values.filter(value => Number.isFinite(value)))];
1356
+ }
1357
+ function readPlanarShadowProjectionStrategy(value, fallback) {
1358
+ return value === 'auto' || value === 'projected-mesh' || value === 'convex-hull'
1359
+ ? value
1360
+ : fallback;
1361
+ }
1362
+ function cloneResolvedOptions(options) {
1363
+ return resolvePlanarShadowOptions(options);
1364
+ }
1365
+ function readPlaneHeightMode(value, fallback) {
1366
+ return value === 'fixed' || value === 'receiver'
1367
+ ? value
1368
+ : fallback === 'fixed' || fallback === 'receiver'
1369
+ ? fallback
1370
+ : 'receiver';
1371
+ }
1372
+ function collectNodeMeshes(root) {
1373
+ const meshes = root.getChildMeshes(false);
1374
+ if (root instanceof Mesh || root instanceof InstancedMesh)
1375
+ meshes.unshift(root);
1376
+ return meshes;
1377
+ }
1378
+ function hasDisablePlanarShadowMetadata(node) {
1379
+ const metadata = node.metadata;
1380
+ return !!metadata
1381
+ && typeof metadata === 'object'
1382
+ && (metadata.disablePlanarShadow === true
1383
+ || metadata.planarShadowInternal === true);
1384
+ }
1385
+ function hasDisabledPlanarShadowReceiver(node, isRootBoundary) {
1386
+ let current = node;
1387
+ while (current) {
1388
+ if (readEditorProjectionShadowReceive(current) === 'none')
1389
+ return true;
1390
+ current = current.parent instanceof AbstractMesh && !isRootBoundary(current.parent)
1391
+ ? current.parent
1392
+ : null;
1393
+ }
1394
+ return false;
1395
+ }
1396
+ function readEditorProjectionShadowReceive(node) {
1397
+ const metadata = node.metadata;
1398
+ if (!metadata || typeof metadata !== 'object')
1399
+ return undefined;
1400
+ const editorProjection = metadata.editorProjection;
1401
+ if (!editorProjection || typeof editorProjection !== 'object')
1402
+ return undefined;
1403
+ const shadow = editorProjection.shadow;
1404
+ if (!shadow || typeof shadow !== 'object')
1405
+ return undefined;
1406
+ return shadow.receive;
1407
+ }
1408
+ function isDisposedNode(node) {
1409
+ return typeof node.isDisposed === 'function' && node.isDisposed();
1410
+ }
1411
+ function readNonNegativeFiniteNumber(value, fallback) {
1412
+ return typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : fallback;
1413
+ }
1414
+ function readMeshMaterial(mesh) {
1415
+ const material = mesh.material;
1416
+ return material && typeof material === 'object'
1417
+ ? material
1418
+ : null;
1419
+ }
1420
+ function resolvePlanarShadowAlphaCutout(mesh) {
1421
+ return findAlphaCutout(mesh.material);
1422
+ }
1423
+ function findAlphaCutout(material) {
1424
+ if (!material || typeof material !== 'object')
1425
+ return null;
1426
+ const record = material;
1427
+ const cutoff = readAlphaCutoff(record.alphaCutOff ?? record.alphaCutoff);
1428
+ const geometryOpacityTexture = readBaseTexture(record.geometryOpacityTexture);
1429
+ if (geometryOpacityTexture && materialUsesOpacityAlpha(record)) {
1430
+ const layer = createPlanarShadowAlphaCutoutLayer(geometryOpacityTexture, 'red', PBR_ALPHA_LUMINANCE_WEIGHTS);
1431
+ return layer ? {
1432
+ cutoff,
1433
+ layers: [layer],
1434
+ } : null;
1435
+ }
1436
+ const surfaceLayer = resolvePlanarShadowSurfaceAlphaLayer(record);
1437
+ const opacityTexture = readBaseTexture(record.opacityTexture);
1438
+ if (opacityTexture && materialUsesOpacityAlpha(record)) {
1439
+ const opacityLayer = createPlanarShadowAlphaCutoutLayer(opacityTexture, opacityTexture.getAlphaFromRGB ? 'luminance' : 'alpha', resolveOpacityLuminanceWeights(record));
1440
+ if (!opacityLayer)
1441
+ return surfaceLayer ? { cutoff, layers: [surfaceLayer] } : null;
1442
+ return {
1443
+ cutoff,
1444
+ layers: surfaceLayer ? [opacityLayer, surfaceLayer] : [opacityLayer],
1445
+ };
1446
+ }
1447
+ if (surfaceLayer)
1448
+ return { cutoff, layers: [surfaceLayer] };
1449
+ const subMaterials = Array.isArray(record.subMaterials) ? record.subMaterials : [];
1450
+ for (const subMaterial of subMaterials) {
1451
+ const resolved = findAlphaCutout(subMaterial);
1452
+ if (resolved)
1453
+ return resolved;
1454
+ }
1455
+ return null;
1456
+ }
1457
+ function resolvePlanarShadowSurfaceAlphaLayer(material) {
1458
+ const albedoTexture = readBaseTexture(material.albedoTexture);
1459
+ if (albedoTexture && materialUsesAlbedoAlpha(material, albedoTexture)) {
1460
+ return createPlanarShadowAlphaCutoutLayer(albedoTexture, 'alpha', STANDARD_ALPHA_LUMINANCE_WEIGHTS, 1);
1461
+ }
1462
+ const diffuseTexture = readBaseTexture(material.diffuseTexture);
1463
+ if (diffuseTexture && materialUsesDiffuseAlpha(material, diffuseTexture)) {
1464
+ return createPlanarShadowAlphaCutoutLayer(diffuseTexture, 'alpha', STANDARD_ALPHA_LUMINANCE_WEIGHTS, 1);
1465
+ }
1466
+ const baseColorTexture = readBaseTexture(material.baseColorTexture);
1467
+ if (baseColorTexture && materialUsesBaseAlpha(material, baseColorTexture)) {
1468
+ return createPlanarShadowAlphaCutoutLayer(baseColorTexture, 'alpha', STANDARD_ALPHA_LUMINANCE_WEIGHTS, 1);
1469
+ }
1470
+ const baseTexture = readBaseTexture(material.baseTexture);
1471
+ if (baseTexture && materialUsesBaseAlpha(material, baseTexture)) {
1472
+ return createPlanarShadowAlphaCutoutLayer(baseTexture, 'alpha', STANDARD_ALPHA_LUMINANCE_WEIGHTS, 1);
1473
+ }
1474
+ return null;
1475
+ }
1476
+ function createPlanarShadowAlphaCutoutLayer(texture, channel, luminanceWeights, level = readTextureLevel(texture)) {
1477
+ const coordinatesIndex = readSupportedTextureCoordinatesIndex(texture);
1478
+ return coordinatesIndex == null
1479
+ ? null
1480
+ : {
1481
+ texture,
1482
+ channel,
1483
+ coordinatesIndex,
1484
+ level,
1485
+ luminanceWeights,
1486
+ };
1487
+ }
1488
+ function readBaseTexture(value) {
1489
+ return value && typeof value === 'object' && typeof value.getClassName === 'function'
1490
+ ? value
1491
+ : null;
1492
+ }
1493
+ function readSupportedTextureCoordinatesIndex(texture) {
1494
+ return texture.coordinatesIndex === 0
1495
+ || texture.coordinatesIndex === 1
1496
+ || texture.coordinatesIndex === 2
1497
+ || texture.coordinatesIndex === 3
1498
+ || texture.coordinatesIndex === 4
1499
+ || texture.coordinatesIndex === 5
1500
+ ? texture.coordinatesIndex
1501
+ : null;
1502
+ }
1503
+ function readTextureLevel(texture) {
1504
+ return typeof texture.level === 'number' && Number.isFinite(texture.level) ? Math.max(0, texture.level) : 1;
1505
+ }
1506
+ function resolveOpacityLuminanceWeights(material) {
1507
+ const className = typeof material.getClassName === 'function'
1508
+ ? String(material.getClassName.call(material))
1509
+ : '';
1510
+ return className.includes('PBR') ? PBR_ALPHA_LUMINANCE_WEIGHTS : STANDARD_ALPHA_LUMINANCE_WEIGHTS;
1511
+ }
1512
+ function materialUsesDiffuseAlpha(material, texture) {
1513
+ return materialAllowsTextureAlpha(material)
1514
+ && texture.hasAlpha === true
1515
+ && (material.useAlphaFromDiffuseTexture === true || materialUsesTextureAlpha(material, texture));
1516
+ }
1517
+ function materialUsesAlbedoAlpha(material, texture) {
1518
+ return materialAllowsTextureAlpha(material)
1519
+ && texture.hasAlpha === true
1520
+ && (material.useAlphaFromAlbedoTexture === true || materialUsesTextureAlpha(material, texture));
1521
+ }
1522
+ function materialUsesBaseAlpha(material, texture) {
1523
+ return materialAllowsTextureAlpha(material)
1524
+ && texture.hasAlpha === true
1525
+ && (material.useAlphaFromBaseColorTexture === true
1526
+ || material._useAlphaFromBaseColorTexture === true
1527
+ || materialUsesTextureAlpha(material, texture));
1528
+ }
1529
+ function materialUsesTextureAlpha(material, texture) {
1530
+ if (texture.hasAlpha !== true)
1531
+ return false;
1532
+ const needAlphaTesting = material.needAlphaTesting;
1533
+ if (typeof needAlphaTesting === 'function' && needAlphaTesting.call(material) === true)
1534
+ return true;
1535
+ return material.transparencyMode === Material.MATERIAL_ALPHATEST
1536
+ || material.transparencyMode === Material.MATERIAL_ALPHATESTANDBLEND;
1537
+ }
1538
+ function materialUsesOpacityAlpha(material) {
1539
+ if (material.transparencyMode === Material.MATERIAL_OPAQUE)
1540
+ return false;
1541
+ const needAlphaTesting = material.needAlphaTesting;
1542
+ if (typeof needAlphaTesting === 'function' && needAlphaTesting.call(material) === true)
1543
+ return true;
1544
+ const needAlphaBlending = material.needAlphaBlending;
1545
+ if (typeof needAlphaBlending === 'function' && needAlphaBlending.call(material) === true)
1546
+ return true;
1547
+ return material.transparencyMode === Material.MATERIAL_ALPHATEST
1548
+ || material.transparencyMode === Material.MATERIAL_ALPHABLEND
1549
+ || material.transparencyMode === Material.MATERIAL_ALPHATESTANDBLEND;
1550
+ }
1551
+ function materialAllowsTextureAlpha(material) {
1552
+ return material.transparencyMode !== Material.MATERIAL_OPAQUE;
1553
+ }
1554
+ function readAlphaCutoff(value) {
1555
+ return typeof value === 'number' && Number.isFinite(value)
1556
+ ? Math.min(1, Math.max(0, value))
1557
+ : 0.1;
1558
+ }
1559
+ function clamp01(value) {
1560
+ return Math.min(1, Math.max(0, value));
1561
+ }
1562
+ //# sourceMappingURL=planar-shadow-system.js.map