@driftengine/core 3.61.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (955) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +9 -0
  3. package/README.md +9 -0
  4. package/dist/behavior/behaviorTree.d.ts +140 -0
  5. package/dist/behavior/behaviorTree.js +289 -0
  6. package/dist/build/shaderComments.d.ts +64 -0
  7. package/dist/build/shaderComments.js +0 -0
  8. package/dist/cinematic/player.d.ts +80 -0
  9. package/dist/cinematic/player.js +184 -0
  10. package/dist/cinematic/script.d.ts +63 -0
  11. package/dist/cinematic/script.js +141 -0
  12. package/dist/core/bootGate.d.ts +37 -0
  13. package/dist/core/bootGate.js +47 -0
  14. package/dist/core/loadTracker.d.ts +78 -0
  15. package/dist/core/loadTracker.js +106 -0
  16. package/dist/core/loop.d.ts +118 -0
  17. package/dist/core/loop.js +84 -0
  18. package/dist/core/messageQueue.d.ts +61 -0
  19. package/dist/core/messageQueue.js +104 -0
  20. package/dist/core/onceSet.d.ts +27 -0
  21. package/dist/core/onceSet.js +62 -0
  22. package/dist/core/preferences.d.ts +47 -0
  23. package/dist/core/preferences.js +140 -0
  24. package/dist/core/remoteSave.d.ts +154 -0
  25. package/dist/core/remoteSave.js +249 -0
  26. package/dist/core/rng.d.ts +64 -0
  27. package/dist/core/rng.js +72 -0
  28. package/dist/core/stepBudget.d.ts +91 -0
  29. package/dist/core/stepBudget.js +105 -0
  30. package/dist/core/storage.d.ts +52 -0
  31. package/dist/core/storage.js +85 -0
  32. package/dist/core/tickTrace.d.ts +72 -0
  33. package/dist/core/tickTrace.js +102 -0
  34. package/dist/core/timeCurve.d.ts +83 -0
  35. package/dist/core/timeCurve.js +107 -0
  36. package/dist/core/withDefaults.d.ts +21 -0
  37. package/dist/core/withDefaults.js +29 -0
  38. package/dist/dev/contactProbe.d.ts +41 -0
  39. package/dist/dev/contactProbe.js +58 -0
  40. package/dist/dev/fpsMeter.d.ts +45 -0
  41. package/dist/dev/fpsMeter.js +86 -0
  42. package/dist/environment/celestialClock.d.ts +141 -0
  43. package/dist/environment/celestialClock.js +262 -0
  44. package/dist/geometry/decal.d.ts +61 -0
  45. package/dist/geometry/decal.js +178 -0
  46. package/dist/geometry/filmPatch.d.ts +35 -0
  47. package/dist/geometry/filmPatch.js +112 -0
  48. package/dist/geometry/growable.d.ts +70 -0
  49. package/dist/geometry/growable.js +167 -0
  50. package/dist/geometry/lightVolume.d.ts +76 -0
  51. package/dist/geometry/lightVolume.js +65 -0
  52. package/dist/geometry/meshBuilder.d.ts +426 -0
  53. package/dist/geometry/meshBuilder.js +1184 -0
  54. package/dist/geometry/pixelFont.d.ts +59 -0
  55. package/dist/geometry/pixelFont.js +179 -0
  56. package/dist/geometry/ribbon.d.ts +101 -0
  57. package/dist/geometry/ribbon.js +546 -0
  58. package/dist/geometry/shellFill.d.ts +101 -0
  59. package/dist/geometry/shellFill.js +203 -0
  60. package/dist/geometry/spline.d.ts +162 -0
  61. package/dist/geometry/spline.js +527 -0
  62. package/dist/geometry/tangents.d.ts +30 -0
  63. package/dist/geometry/tangents.js +142 -0
  64. package/dist/geometry/textMesh.d.ts +51 -0
  65. package/dist/geometry/textMesh.js +57 -0
  66. package/dist/geometry/treeBuilder.d.ts +37 -0
  67. package/dist/geometry/treeBuilder.js +56 -0
  68. package/dist/host/display.d.ts +135 -0
  69. package/dist/host/display.js +114 -0
  70. package/dist/host/files.d.ts +25 -0
  71. package/dist/host/files.js +44 -0
  72. package/dist/host/lifecycle.d.ts +45 -0
  73. package/dist/host/lifecycle.js +28 -0
  74. package/dist/host/platformServices.d.ts +23 -0
  75. package/dist/host/platformServices.js +1 -0
  76. package/dist/host/screen.d.ts +49 -0
  77. package/dist/host/screen.js +90 -0
  78. package/dist/index.d.ts +289 -0
  79. package/dist/index.js +320 -0
  80. package/dist/input/actionMap.d.ts +181 -0
  81. package/dist/input/actionMap.js +314 -0
  82. package/dist/input/activeDevice.d.ts +55 -0
  83. package/dist/input/activeDevice.js +83 -0
  84. package/dist/input/gamepadIdentity.d.ts +43 -0
  85. package/dist/input/gamepadIdentity.js +108 -0
  86. package/dist/input/gamepadMapping.d.ts +56 -0
  87. package/dist/input/gamepadMapping.js +91 -0
  88. package/dist/input/input.d.ts +281 -0
  89. package/dist/input/input.js +703 -0
  90. package/dist/input/inputHarness.d.ts +42 -0
  91. package/dist/input/inputHarness.js +193 -0
  92. package/dist/input/rightGesture.d.ts +57 -0
  93. package/dist/input/rightGesture.js +49 -0
  94. package/dist/input/rumble.d.ts +71 -0
  95. package/dist/input/rumble.js +119 -0
  96. package/dist/input/touchControls.d.ts +138 -0
  97. package/dist/input/touchControls.js +352 -0
  98. package/dist/input/typingTarget.d.ts +6 -0
  99. package/dist/input/typingTarget.js +27 -0
  100. package/dist/math/bounds.d.ts +61 -0
  101. package/dist/math/bounds.js +121 -0
  102. package/dist/math/color.d.ts +8 -0
  103. package/dist/math/color.js +35 -0
  104. package/dist/math/exact.d.ts +66 -0
  105. package/dist/math/exact.js +262 -0
  106. package/dist/math/frustum.d.ts +44 -0
  107. package/dist/math/frustum.js +90 -0
  108. package/dist/math/intersect.d.ts +76 -0
  109. package/dist/math/intersect.js +185 -0
  110. package/dist/math/scalar.d.ts +51 -0
  111. package/dist/math/scalar.js +99 -0
  112. package/dist/math/spring.d.ts +59 -0
  113. package/dist/math/spring.js +88 -0
  114. package/dist/nav/navGraph.d.ts +71 -0
  115. package/dist/nav/navGraph.js +111 -0
  116. package/dist/nav/navPath.d.ts +93 -0
  117. package/dist/nav/navPath.js +162 -0
  118. package/dist/nav/navSearch.d.ts +74 -0
  119. package/dist/nav/navSearch.js +198 -0
  120. package/dist/physics/boxSurface.d.ts +48 -0
  121. package/dist/physics/boxSurface.js +73 -0
  122. package/dist/physics/colliderSurface.d.ts +46 -0
  123. package/dist/physics/colliderSurface.js +146 -0
  124. package/dist/physics/compositeSurface.d.ts +33 -0
  125. package/dist/physics/compositeSurface.js +108 -0
  126. package/dist/physics/heightSurface.d.ts +103 -0
  127. package/dist/physics/heightSurface.js +153 -0
  128. package/dist/physics/ribbonSurface.d.ts +239 -0
  129. package/dist/physics/ribbonSurface.js +522 -0
  130. package/dist/render/ambientOcclusionPass.d.ts +53 -0
  131. package/dist/render/ambientOcclusionPass.js +179 -0
  132. package/dist/render/areaLights.d.ts +116 -0
  133. package/dist/render/areaLights.js +91 -0
  134. package/dist/render/areaShadowSet.d.ts +184 -0
  135. package/dist/render/areaShadowSet.js +277 -0
  136. package/dist/render/atmosphere.d.ts +94 -0
  137. package/dist/render/atmosphere.js +91 -0
  138. package/dist/render/backend/api.d.ts +496 -0
  139. package/dist/render/backend/api.js +1 -0
  140. package/dist/render/backend/budget.d.ts +82 -0
  141. package/dist/render/backend/budget.js +99 -0
  142. package/dist/render/backend/createRenderer.d.ts +135 -0
  143. package/dist/render/backend/createRenderer.js +201 -0
  144. package/dist/render/backend/deadline.d.ts +24 -0
  145. package/dist/render/backend/deadline.js +38 -0
  146. package/dist/render/backend/probe.d.ts +30 -0
  147. package/dist/render/backend/probe.js +129 -0
  148. package/dist/render/backend/select.d.ts +70 -0
  149. package/dist/render/backend/select.js +249 -0
  150. package/dist/render/backend/timer.d.ts +50 -0
  151. package/dist/render/backend/timer.js +1 -0
  152. package/dist/render/backend/webgl2/instanced.d.ts +21 -0
  153. package/dist/render/backend/webgl2/instanced.js +42 -0
  154. package/dist/render/backend/webgl2/morphTexture.d.ts +21 -0
  155. package/dist/render/backend/webgl2/morphTexture.js +55 -0
  156. package/dist/render/backend/webgl2/renderer.d.ts +3027 -0
  157. package/dist/render/backend/webgl2/renderer.js +6041 -0
  158. package/dist/render/backend/webgl2/skinPaletteTexture.d.ts +32 -0
  159. package/dist/render/backend/webgl2/skinPaletteTexture.js +68 -0
  160. package/dist/render/backend/webgpu/boltPass.d.ts +42 -0
  161. package/dist/render/backend/webgpu/boltPass.js +160 -0
  162. package/dist/render/backend/webgpu/buffers.d.ts +159 -0
  163. package/dist/render/backend/webgpu/buffers.js +402 -0
  164. package/dist/render/backend/webgpu/causticsPass.d.ts +36 -0
  165. package/dist/render/backend/webgpu/causticsPass.js +138 -0
  166. package/dist/render/backend/webgpu/clusterBinner.d.ts +51 -0
  167. package/dist/render/backend/webgpu/clusterBinner.js +206 -0
  168. package/dist/render/backend/webgpu/depthPass.d.ts +114 -0
  169. package/dist/render/backend/webgpu/depthPass.js +228 -0
  170. package/dist/render/backend/webgpu/device.d.ts +52 -0
  171. package/dist/render/backend/webgpu/device.js +121 -0
  172. package/dist/render/backend/webgpu/drawCommand.d.ts +43 -0
  173. package/dist/render/backend/webgpu/drawCommand.js +54 -0
  174. package/dist/render/backend/webgpu/environmentTexels.d.ts +28 -0
  175. package/dist/render/backend/webgpu/environmentTexels.js +79 -0
  176. package/dist/render/backend/webgpu/filmPass.d.ts +29 -0
  177. package/dist/render/backend/webgpu/filmPass.js +97 -0
  178. package/dist/render/backend/webgpu/flatPass.d.ts +213 -0
  179. package/dist/render/backend/webgpu/flatPass.js +482 -0
  180. package/dist/render/backend/webgpu/flockPass.d.ts +34 -0
  181. package/dist/render/backend/webgpu/flockPass.js +129 -0
  182. package/dist/render/backend/webgpu/gpuTimestamps.d.ts +94 -0
  183. package/dist/render/backend/webgpu/gpuTimestamps.js +213 -0
  184. package/dist/render/backend/webgpu/insetPass.d.ts +19 -0
  185. package/dist/render/backend/webgpu/insetPass.js +108 -0
  186. package/dist/render/backend/webgpu/instanced.d.ts +27 -0
  187. package/dist/render/backend/webgpu/instanced.js +49 -0
  188. package/dist/render/backend/webgpu/lightVolumePass.d.ts +75 -0
  189. package/dist/render/backend/webgpu/lightVolumePass.js +154 -0
  190. package/dist/render/backend/webgpu/linePass.d.ts +90 -0
  191. package/dist/render/backend/webgpu/linePass.js +220 -0
  192. package/dist/render/backend/webgpu/morphTexture.d.ts +18 -0
  193. package/dist/render/backend/webgpu/morphTexture.js +51 -0
  194. package/dist/render/backend/webgpu/panelPass.d.ts +37 -0
  195. package/dist/render/backend/webgpu/panelPass.js +114 -0
  196. package/dist/render/backend/webgpu/particlePass.d.ts +89 -0
  197. package/dist/render/backend/webgpu/particlePass.js +280 -0
  198. package/dist/render/backend/webgpu/pipelineCache.d.ts +93 -0
  199. package/dist/render/backend/webgpu/pipelineCache.js +140 -0
  200. package/dist/render/backend/webgpu/plumePass.d.ts +69 -0
  201. package/dist/render/backend/webgpu/plumePass.js +213 -0
  202. package/dist/render/backend/webgpu/pointShadowPass.d.ts +88 -0
  203. package/dist/render/backend/webgpu/pointShadowPass.js +280 -0
  204. package/dist/render/backend/webgpu/postPass.d.ts +342 -0
  205. package/dist/render/backend/webgpu/postPass.js +366 -0
  206. package/dist/render/backend/webgpu/probePass.d.ts +108 -0
  207. package/dist/render/backend/webgpu/probePass.js +188 -0
  208. package/dist/render/backend/webgpu/reflectionPass.d.ts +29 -0
  209. package/dist/render/backend/webgpu/reflectionPass.js +87 -0
  210. package/dist/render/backend/webgpu/renderer.d.ts +3082 -0
  211. package/dist/render/backend/webgpu/renderer.js +10911 -0
  212. package/dist/render/backend/webgpu/scatterPass.d.ts +95 -0
  213. package/dist/render/backend/webgpu/scatterPass.js +266 -0
  214. package/dist/render/backend/webgpu/sdfTextPass.d.ts +86 -0
  215. package/dist/render/backend/webgpu/sdfTextPass.js +190 -0
  216. package/dist/render/backend/webgpu/shaderModules.d.ts +9 -0
  217. package/dist/render/backend/webgpu/shaderModules.js +45 -0
  218. package/dist/render/backend/webgpu/shaders/clusterBin.wgsl.d.ts +23 -0
  219. package/dist/render/backend/webgpu/shaders/clusterBin.wgsl.js +208 -0
  220. package/dist/render/backend/webgpu/skinPaletteRing.d.ts +67 -0
  221. package/dist/render/backend/webgpu/skinPaletteRing.js +97 -0
  222. package/dist/render/backend/webgpu/skinPaletteTexture.d.ts +17 -0
  223. package/dist/render/backend/webgpu/skinPaletteTexture.js +68 -0
  224. package/dist/render/backend/webgpu/skyPass.d.ts +102 -0
  225. package/dist/render/backend/webgpu/skyPass.js +70 -0
  226. package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +41 -0
  227. package/dist/render/backend/webgpu/surfaceTexturePass.js +228 -0
  228. package/dist/render/backend/webgpu/textPass.d.ts +73 -0
  229. package/dist/render/backend/webgpu/textPass.js +190 -0
  230. package/dist/render/backend/webgpu/uniformRing.d.ts +133 -0
  231. package/dist/render/backend/webgpu/uniformRing.js +208 -0
  232. package/dist/render/backend/webgpu/waterPass.d.ts +73 -0
  233. package/dist/render/backend/webgpu/waterPass.js +160 -0
  234. package/dist/render/backend/webgpu/windStreakPass.d.ts +46 -0
  235. package/dist/render/backend/webgpu/windStreakPass.js +153 -0
  236. package/dist/render/billboard.d.ts +27 -0
  237. package/dist/render/billboard.js +79 -0
  238. package/dist/render/bloomChain.d.ts +66 -0
  239. package/dist/render/bloomChain.js +83 -0
  240. package/dist/render/bloomPass.d.ts +54 -0
  241. package/dist/render/bloomPass.js +219 -0
  242. package/dist/render/boltBatch.d.ts +39 -0
  243. package/dist/render/boltBatch.js +121 -0
  244. package/dist/render/boltPool.d.ts +135 -0
  245. package/dist/render/boltPool.js +259 -0
  246. package/dist/render/boom.d.ts +46 -0
  247. package/dist/render/boom.js +81 -0
  248. package/dist/render/camera.d.ts +119 -0
  249. package/dist/render/camera.js +304 -0
  250. package/dist/render/causticsRenderer.d.ts +63 -0
  251. package/dist/render/causticsRenderer.js +119 -0
  252. package/dist/render/cinematicCamera.d.ts +102 -0
  253. package/dist/render/cinematicCamera.js +227 -0
  254. package/dist/render/clusteredLights.d.ts +260 -0
  255. package/dist/render/clusteredLights.js +479 -0
  256. package/dist/render/colourGrade.d.ts +62 -0
  257. package/dist/render/colourGrade.js +91 -0
  258. package/dist/render/compute.d.ts +74 -0
  259. package/dist/render/compute.js +8 -0
  260. package/dist/render/contextLoss.d.ts +32 -0
  261. package/dist/render/contextLoss.js +46 -0
  262. package/dist/render/debugLines.d.ts +75 -0
  263. package/dist/render/debugLines.js +281 -0
  264. package/dist/render/decalPass.d.ts +34 -0
  265. package/dist/render/decalPass.js +103 -0
  266. package/dist/render/decalProjector.d.ts +140 -0
  267. package/dist/render/decalProjector.js +282 -0
  268. package/dist/render/decalQueue.d.ts +53 -0
  269. package/dist/render/decalQueue.js +87 -0
  270. package/dist/render/depthConvention.d.ts +314 -0
  271. package/dist/render/depthConvention.js +346 -0
  272. package/dist/render/drawingBuffer.d.ts +52 -0
  273. package/dist/render/drawingBuffer.js +67 -0
  274. package/dist/render/emptyTexture.d.ts +60 -0
  275. package/dist/render/emptyTexture.js +103 -0
  276. package/dist/render/envProbeArray.d.ts +85 -0
  277. package/dist/render/envProbeArray.js +164 -0
  278. package/dist/render/equirectToCube.d.ts +54 -0
  279. package/dist/render/equirectToCube.js +123 -0
  280. package/dist/render/flockGeometry.d.ts +23 -0
  281. package/dist/render/flockGeometry.js +43 -0
  282. package/dist/render/flockRenderer.d.ts +37 -0
  283. package/dist/render/flockRenderer.js +70 -0
  284. package/dist/render/frame/arena.d.ts +32 -0
  285. package/dist/render/frame/arena.js +65 -0
  286. package/dist/render/frame/index.d.ts +18 -0
  287. package/dist/render/frame/index.js +4 -0
  288. package/dist/render/frame/replay.d.ts +34 -0
  289. package/dist/render/frame/replay.js +48 -0
  290. package/dist/render/frame/resources.d.ts +25 -0
  291. package/dist/render/frame/resources.js +55 -0
  292. package/dist/render/frame/schedule.d.ts +37 -0
  293. package/dist/render/frame/schedule.js +61 -0
  294. package/dist/render/frameView.d.ts +19 -0
  295. package/dist/render/frameView.js +1 -0
  296. package/dist/render/gizmo.d.ts +236 -0
  297. package/dist/render/gizmo.js +727 -0
  298. package/dist/render/globalMedium.d.ts +124 -0
  299. package/dist/render/globalMedium.js +100 -0
  300. package/dist/render/globalMediumPass.d.ts +101 -0
  301. package/dist/render/globalMediumPass.js +250 -0
  302. package/dist/render/gpuCapability.d.ts +111 -0
  303. package/dist/render/gpuCapability.js +194 -0
  304. package/dist/render/gpuTimer.d.ts +87 -0
  305. package/dist/render/gpuTimer.js +145 -0
  306. package/dist/render/halfFloat.d.ts +4 -0
  307. package/dist/render/halfFloat.js +38 -0
  308. package/dist/render/handleRegistry.d.ts +54 -0
  309. package/dist/render/handleRegistry.js +84 -0
  310. package/dist/render/iesProfile.d.ts +64 -0
  311. package/dist/render/iesProfile.js +179 -0
  312. package/dist/render/imageTexels.d.ts +50 -0
  313. package/dist/render/imageTexels.js +91 -0
  314. package/dist/render/instancedMesh.d.ts +48 -0
  315. package/dist/render/instancedMesh.js +127 -0
  316. package/dist/render/instances.d.ts +69 -0
  317. package/dist/render/instances.js +52 -0
  318. package/dist/render/lightBudget.d.ts +397 -0
  319. package/dist/render/lightBudget.js +524 -0
  320. package/dist/render/lightMatrix.d.ts +3 -0
  321. package/dist/render/lightMatrix.js +45 -0
  322. package/dist/render/lightVolumeDraw.d.ts +132 -0
  323. package/dist/render/lightVolumeDraw.js +181 -0
  324. package/dist/render/lineBatch.d.ts +60 -0
  325. package/dist/render/lineBatch.js +160 -0
  326. package/dist/render/linePoints.d.ts +33 -0
  327. package/dist/render/linePoints.js +46 -0
  328. package/dist/render/livePointShadowSet.d.ts +72 -0
  329. package/dist/render/livePointShadowSet.js +258 -0
  330. package/dist/render/lod.d.ts +35 -0
  331. package/dist/render/lod.js +65 -0
  332. package/dist/render/mesh.d.ts +177 -0
  333. package/dist/render/mesh.js +466 -0
  334. package/dist/render/occlusion.d.ts +81 -0
  335. package/dist/render/occlusion.js +378 -0
  336. package/dist/render/oitPass.d.ts +67 -0
  337. package/dist/render/oitPass.js +213 -0
  338. package/dist/render/orderIndependent.d.ts +92 -0
  339. package/dist/render/orderIndependent.js +108 -0
  340. package/dist/render/particleBatch.d.ts +140 -0
  341. package/dist/render/particleBatch.js +186 -0
  342. package/dist/render/particleMaterial.d.ts +54 -0
  343. package/dist/render/particleMaterial.js +42 -0
  344. package/dist/render/particlePool.d.ts +124 -0
  345. package/dist/render/particlePool.js +182 -0
  346. package/dist/render/pass.d.ts +195 -0
  347. package/dist/render/pass.js +9 -0
  348. package/dist/render/passTarget.d.ts +86 -0
  349. package/dist/render/passTarget.js +121 -0
  350. package/dist/render/pickable.d.ts +31 -0
  351. package/dist/render/pickable.js +172 -0
  352. package/dist/render/planarReflection.d.ts +59 -0
  353. package/dist/render/planarReflection.js +194 -0
  354. package/dist/render/planarReflectionDraw.d.ts +31 -0
  355. package/dist/render/planarReflectionDraw.js +65 -0
  356. package/dist/render/plumeGeometry.d.ts +25 -0
  357. package/dist/render/plumeGeometry.js +61 -0
  358. package/dist/render/plumeMaterial.d.ts +31 -0
  359. package/dist/render/plumeMaterial.js +64 -0
  360. package/dist/render/plumeRenderer.d.ts +96 -0
  361. package/dist/render/plumeRenderer.js +185 -0
  362. package/dist/render/pointLightSelection.d.ts +175 -0
  363. package/dist/render/pointLightSelection.js +301 -0
  364. package/dist/render/pointShadowArray.d.ts +95 -0
  365. package/dist/render/pointShadowArray.js +219 -0
  366. package/dist/render/pointShadowBudget.d.ts +118 -0
  367. package/dist/render/pointShadowBudget.js +221 -0
  368. package/dist/render/pointShadowImage.d.ts +265 -0
  369. package/dist/render/pointShadowImage.js +339 -0
  370. package/dist/render/pointShadowMap.d.ts +93 -0
  371. package/dist/render/pointShadowMap.js +167 -0
  372. package/dist/render/pointShadowSystem.d.ts +195 -0
  373. package/dist/render/pointShadowSystem.js +336 -0
  374. package/dist/render/prefilterEnvMap.d.ts +124 -0
  375. package/dist/render/prefilterEnvMap.js +146 -0
  376. package/dist/render/prefilterPass.d.ts +44 -0
  377. package/dist/render/prefilterPass.js +98 -0
  378. package/dist/render/probeGrid.d.ts +122 -0
  379. package/dist/render/probeGrid.js +174 -0
  380. package/dist/render/rainField.d.ts +99 -0
  381. package/dist/render/rainField.js +168 -0
  382. package/dist/render/reflectionProbe.d.ts +188 -0
  383. package/dist/render/reflectionProbe.js +437 -0
  384. package/dist/render/renderQuality.d.ts +699 -0
  385. package/dist/render/renderQuality.js +293 -0
  386. package/dist/render/rendererHarness.d.ts +39 -0
  387. package/dist/render/rendererHarness.js +185 -0
  388. package/dist/render/resolutionGovernor.d.ts +107 -0
  389. package/dist/render/resolutionGovernor.js +144 -0
  390. package/dist/render/scatterDeform.d.ts +17 -0
  391. package/dist/render/scatterDeform.js +46 -0
  392. package/dist/render/sceneTarget.d.ts +288 -0
  393. package/dist/render/sceneTarget.js +856 -0
  394. package/dist/render/screenSpaceReflection.d.ts +233 -0
  395. package/dist/render/screenSpaceReflection.js +415 -0
  396. package/dist/render/sdfFont.d.ts +77 -0
  397. package/dist/render/sdfFont.js +86 -0
  398. package/dist/render/sdfTextLayout.d.ts +46 -0
  399. package/dist/render/sdfTextLayout.js +145 -0
  400. package/dist/render/sdfTextRenderer.d.ts +75 -0
  401. package/dist/render/sdfTextRenderer.js +194 -0
  402. package/dist/render/seaState.d.ts +25 -0
  403. package/dist/render/seaState.js +34 -0
  404. package/dist/render/segmentQuads.d.ts +29 -0
  405. package/dist/render/segmentQuads.js +75 -0
  406. package/dist/render/shader.d.ts +5 -0
  407. package/dist/render/shader.js +172 -0
  408. package/dist/render/shaders/ambientOcclusion.d.ts +45 -0
  409. package/dist/render/shaders/ambientOcclusion.js +352 -0
  410. package/dist/render/shaders/arcane.d.ts +19 -0
  411. package/dist/render/shaders/arcane.js +102 -0
  412. package/dist/render/shaders/bloom.d.ts +70 -0
  413. package/dist/render/shaders/bloom.js +227 -0
  414. package/dist/render/shaders/bolt.d.ts +17 -0
  415. package/dist/render/shaders/bolt.js +161 -0
  416. package/dist/render/shaders/caustics.d.ts +38 -0
  417. package/dist/render/shaders/caustics.js +167 -0
  418. package/dist/render/shaders/conditionals.d.ts +21 -0
  419. package/dist/render/shaders/conditionals.js +55 -0
  420. package/dist/render/shaders/decalProject.d.ts +25 -0
  421. package/dist/render/shaders/decalProject.js +125 -0
  422. package/dist/render/shaders/depth.d.ts +14 -0
  423. package/dist/render/shaders/depth.js +114 -0
  424. package/dist/render/shaders/depthOfField.d.ts +41 -0
  425. package/dist/render/shaders/depthOfField.js +139 -0
  426. package/dist/render/shaders/film.d.ts +15 -0
  427. package/dist/render/shaders/film.js +283 -0
  428. package/dist/render/shaders/fire.d.ts +12 -0
  429. package/dist/render/shaders/fire.js +178 -0
  430. package/dist/render/shaders/flat/directionalShadow.d.ts +1 -0
  431. package/dist/render/shaders/flat/directionalShadow.js +195 -0
  432. package/dist/render/shaders/flat/index.d.ts +135 -0
  433. package/dist/render/shaders/flat/index.js +387 -0
  434. package/dist/render/shaders/flat/lobes.d.ts +1 -0
  435. package/dist/render/shaders/flat/lobes.js +398 -0
  436. package/dist/render/shaders/flat/main.d.ts +2 -0
  437. package/dist/render/shaders/flat/main.js +1630 -0
  438. package/dist/render/shaders/flat/pointShadow.d.ts +1 -0
  439. package/dist/render/shaders/flat/pointShadow.js +322 -0
  440. package/dist/render/shaders/flat/preamble.d.ts +15 -0
  441. package/dist/render/shaders/flat/preamble.js +864 -0
  442. package/dist/render/shaders/flat/probeGrid.d.ts +21 -0
  443. package/dist/render/shaders/flat/probeGrid.js +151 -0
  444. package/dist/render/shaders/flat/surface.d.ts +2 -0
  445. package/dist/render/shaders/flat/surface.js +40 -0
  446. package/dist/render/shaders/flock.d.ts +11 -0
  447. package/dist/render/shaders/flock.js +102 -0
  448. package/dist/render/shaders/fog.d.ts +37 -0
  449. package/dist/render/shaders/fog.js +98 -0
  450. package/dist/render/shaders/fullscreen.d.ts +16 -0
  451. package/dist/render/shaders/fullscreen.js +27 -0
  452. package/dist/render/shaders/generated/ambientOcclusion.wgsl.d.ts +65 -0
  453. package/dist/render/shaders/generated/ambientOcclusion.wgsl.js +71 -0
  454. package/dist/render/shaders/generated/arcane.wgsl.d.ts +90 -0
  455. package/dist/render/shaders/generated/arcane.wgsl.js +96 -0
  456. package/dist/render/shaders/generated/bloom.wgsl.d.ts +69 -0
  457. package/dist/render/shaders/generated/bloom.wgsl.js +75 -0
  458. package/dist/render/shaders/generated/bolt.wgsl.d.ts +118 -0
  459. package/dist/render/shaders/generated/bolt.wgsl.js +124 -0
  460. package/dist/render/shaders/generated/caustics.wgsl.d.ts +118 -0
  461. package/dist/render/shaders/generated/caustics.wgsl.js +124 -0
  462. package/dist/render/shaders/generated/decalProject.wgsl.d.ts +61 -0
  463. package/dist/render/shaders/generated/decalProject.wgsl.js +67 -0
  464. package/dist/render/shaders/generated/depth.wgsl.d.ts +131 -0
  465. package/dist/render/shaders/generated/depth.wgsl.js +137 -0
  466. package/dist/render/shaders/generated/film.wgsl.d.ts +134 -0
  467. package/dist/render/shaders/generated/film.wgsl.js +140 -0
  468. package/dist/render/shaders/generated/fire.wgsl.d.ts +128 -0
  469. package/dist/render/shaders/generated/fire.wgsl.js +134 -0
  470. package/dist/render/shaders/generated/flat.wgsl.d.ts +9163 -0
  471. package/dist/render/shaders/generated/flat.wgsl.js +9192 -0
  472. package/dist/render/shaders/generated/flock.wgsl.d.ts +73 -0
  473. package/dist/render/shaders/generated/flock.wgsl.js +79 -0
  474. package/dist/render/shaders/generated/fullscreen.wgsl.d.ts +12 -0
  475. package/dist/render/shaders/generated/fullscreen.wgsl.js +18 -0
  476. package/dist/render/shaders/generated/globalMedium.wgsl.d.ts +130 -0
  477. package/dist/render/shaders/generated/globalMedium.wgsl.js +136 -0
  478. package/dist/render/shaders/generated/lightVolume.wgsl.d.ts +230 -0
  479. package/dist/render/shaders/generated/lightVolume.wgsl.js +239 -0
  480. package/dist/render/shaders/generated/line.wgsl.d.ts +128 -0
  481. package/dist/render/shaders/generated/line.wgsl.js +134 -0
  482. package/dist/render/shaders/generated/octahedralResolve.wgsl.d.ts +51 -0
  483. package/dist/render/shaders/generated/octahedralResolve.wgsl.js +57 -0
  484. package/dist/render/shaders/generated/oitResolve.wgsl.d.ts +23 -0
  485. package/dist/render/shaders/generated/oitResolve.wgsl.js +29 -0
  486. package/dist/render/shaders/generated/panel.wgsl.d.ts +48 -0
  487. package/dist/render/shaders/generated/panel.wgsl.js +54 -0
  488. package/dist/render/shaders/generated/particle.wgsl.d.ts +332 -0
  489. package/dist/render/shaders/generated/particle.wgsl.js +338 -0
  490. package/dist/render/shaders/generated/prefilter.wgsl.d.ts +61 -0
  491. package/dist/render/shaders/generated/prefilter.wgsl.js +67 -0
  492. package/dist/render/shaders/generated/rush.wgsl.d.ts +131 -0
  493. package/dist/render/shaders/generated/rush.wgsl.js +137 -0
  494. package/dist/render/shaders/generated/scatter.wgsl.d.ts +195 -0
  495. package/dist/render/shaders/generated/scatter.wgsl.js +201 -0
  496. package/dist/render/shaders/generated/sdfText.wgsl.d.ts +69 -0
  497. package/dist/render/shaders/generated/sdfText.wgsl.js +75 -0
  498. package/dist/render/shaders/generated/sky.wgsl.d.ts +100 -0
  499. package/dist/render/shaders/generated/sky.wgsl.js +106 -0
  500. package/dist/render/shaders/generated/smoke.wgsl.d.ts +85 -0
  501. package/dist/render/shaders/generated/smoke.wgsl.js +91 -0
  502. package/dist/render/shaders/generated/ssrResolve.wgsl.d.ts +18 -0
  503. package/dist/render/shaders/generated/ssrResolve.wgsl.js +24 -0
  504. package/dist/render/shaders/generated/ssrTrace.wgsl.d.ts +86 -0
  505. package/dist/render/shaders/generated/ssrTrace.wgsl.js +92 -0
  506. package/dist/render/shaders/generated/temporalResolve.wgsl.d.ts +46 -0
  507. package/dist/render/shaders/generated/temporalResolve.wgsl.js +52 -0
  508. package/dist/render/shaders/generated/text.wgsl.d.ts +98 -0
  509. package/dist/render/shaders/generated/text.wgsl.js +104 -0
  510. package/dist/render/shaders/generated/water.wgsl.d.ts +247 -0
  511. package/dist/render/shaders/generated/water.wgsl.js +253 -0
  512. package/dist/render/shaders/generated/windStreaks.wgsl.d.ts +73 -0
  513. package/dist/render/shaders/generated/windStreaks.wgsl.js +79 -0
  514. package/dist/render/shaders/gerstner.d.ts +21 -0
  515. package/dist/render/shaders/gerstner.js +110 -0
  516. package/dist/render/shaders/globalMedium.d.ts +56 -0
  517. package/dist/render/shaders/globalMedium.js +393 -0
  518. package/dist/render/shaders/lightVolume.d.ts +61 -0
  519. package/dist/render/shaders/lightVolume.js +527 -0
  520. package/dist/render/shaders/line.d.ts +18 -0
  521. package/dist/render/shaders/line.js +122 -0
  522. package/dist/render/shaders/morph.d.ts +26 -0
  523. package/dist/render/shaders/morph.js +61 -0
  524. package/dist/render/shaders/octahedral.d.ts +76 -0
  525. package/dist/render/shaders/octahedral.js +161 -0
  526. package/dist/render/shaders/octahedralResolve.d.ts +25 -0
  527. package/dist/render/shaders/octahedralResolve.js +81 -0
  528. package/dist/render/shaders/oitResolve.d.ts +17 -0
  529. package/dist/render/shaders/oitResolve.js +42 -0
  530. package/dist/render/shaders/outputTransform.d.ts +37 -0
  531. package/dist/render/shaders/outputTransform.js +106 -0
  532. package/dist/render/shaders/panel.d.ts +17 -0
  533. package/dist/render/shaders/panel.js +50 -0
  534. package/dist/render/shaders/particle.d.ts +82 -0
  535. package/dist/render/shaders/particle.js +431 -0
  536. package/dist/render/shaders/prefilter.d.ts +37 -0
  537. package/dist/render/shaders/prefilter.js +276 -0
  538. package/dist/render/shaders/rush.d.ts +21 -0
  539. package/dist/render/shaders/rush.js +355 -0
  540. package/dist/render/shaders/scatter.d.ts +14 -0
  541. package/dist/render/shaders/scatter.js +206 -0
  542. package/dist/render/shaders/sdfText.d.ts +9 -0
  543. package/dist/render/shaders/sdfText.js +65 -0
  544. package/dist/render/shaders/skinning.d.ts +17 -0
  545. package/dist/render/shaders/skinning.js +59 -0
  546. package/dist/render/shaders/sky.d.ts +10 -0
  547. package/dist/render/shaders/sky.js +242 -0
  548. package/dist/render/shaders/smoke.d.ts +8 -0
  549. package/dist/render/shaders/smoke.js +88 -0
  550. package/dist/render/shaders/ssrResolve.d.ts +15 -0
  551. package/dist/render/shaders/ssrResolve.js +28 -0
  552. package/dist/render/shaders/ssrTrace.d.ts +22 -0
  553. package/dist/render/shaders/ssrTrace.js +230 -0
  554. package/dist/render/shaders/tangentFrame.d.ts +17 -0
  555. package/dist/render/shaders/tangentFrame.js +70 -0
  556. package/dist/render/shaders/temporalResolve.d.ts +1 -0
  557. package/dist/render/shaders/temporalResolve.js +150 -0
  558. package/dist/render/shaders/text.d.ts +17 -0
  559. package/dist/render/shaders/text.js +155 -0
  560. package/dist/render/shaders/vertexChannel.d.ts +39 -0
  561. package/dist/render/shaders/vertexChannel.js +69 -0
  562. package/dist/render/shaders/water.d.ts +18 -0
  563. package/dist/render/shaders/water.js +354 -0
  564. package/dist/render/shaders/windStreaks.d.ts +14 -0
  565. package/dist/render/shaders/windStreaks.js +129 -0
  566. package/dist/render/shadowCasters.d.ts +107 -0
  567. package/dist/render/shadowCasters.js +1 -0
  568. package/dist/render/shadowMap.d.ts +10 -0
  569. package/dist/render/shadowMap.js +80 -0
  570. package/dist/render/shotPlacement.d.ts +80 -0
  571. package/dist/render/shotPlacement.js +183 -0
  572. package/dist/render/skinPalette.d.ts +90 -0
  573. package/dist/render/skinPalette.js +136 -0
  574. package/dist/render/ssrPass.d.ts +59 -0
  575. package/dist/render/ssrPass.js +196 -0
  576. package/dist/render/surfaceSheet.d.ts +52 -0
  577. package/dist/render/surfaceSheet.js +99 -0
  578. package/dist/render/surfaceTexture.d.ts +197 -0
  579. package/dist/render/surfaceTexture.js +116 -0
  580. package/dist/render/temporalAa.d.ts +104 -0
  581. package/dist/render/temporalAa.js +194 -0
  582. package/dist/render/temporalPass.d.ts +54 -0
  583. package/dist/render/temporalPass.js +135 -0
  584. package/dist/render/textLayout.d.ts +88 -0
  585. package/dist/render/textLayout.js +201 -0
  586. package/dist/render/textRenderer.d.ts +32 -0
  587. package/dist/render/textRenderer.js +176 -0
  588. package/dist/render/thirdPersonCamera.d.ts +272 -0
  589. package/dist/render/thirdPersonCamera.js +479 -0
  590. package/dist/render/trample.d.ts +57 -0
  591. package/dist/render/trample.js +134 -0
  592. package/dist/render/translucentQueue.d.ts +76 -0
  593. package/dist/render/translucentQueue.js +90 -0
  594. package/dist/render/uniformVectorBudget.d.ts +105 -0
  595. package/dist/render/uniformVectorBudget.js +201 -0
  596. package/dist/render/uploadStep.d.ts +14 -0
  597. package/dist/render/uploadStep.js +14 -0
  598. package/dist/render/vertexDefaults.d.ts +48 -0
  599. package/dist/render/vertexDefaults.js +111 -0
  600. package/dist/render/visibility.d.ts +22 -0
  601. package/dist/render/visibility.js +47 -0
  602. package/dist/render/waterDraw.d.ts +57 -0
  603. package/dist/render/waterDraw.js +102 -0
  604. package/dist/render/waterGrid.d.ts +62 -0
  605. package/dist/render/waterGrid.js +151 -0
  606. package/dist/render/waterRenderer.d.ts +163 -0
  607. package/dist/render/waterRenderer.js +165 -0
  608. package/dist/render/wind.d.ts +45 -0
  609. package/dist/render/wind.js +32 -0
  610. package/dist/render/windField.d.ts +34 -0
  611. package/dist/render/windField.js +13 -0
  612. package/dist/render/windStreakDraw.d.ts +73 -0
  613. package/dist/render/windStreakDraw.js +86 -0
  614. package/dist/render/windStreakRenderer.d.ts +37 -0
  615. package/dist/render/windStreakRenderer.js +86 -0
  616. package/dist/scene/node.d.ts +128 -0
  617. package/dist/scene/node.js +319 -0
  618. package/dist/scene/visit.d.ts +38 -0
  619. package/dist/scene/visit.js +43 -0
  620. package/dist/ui/clipMime.d.ts +46 -0
  621. package/dist/ui/clipMime.js +88 -0
  622. package/dist/ui/exportTarget.d.ts +305 -0
  623. package/dist/ui/exportTarget.js +368 -0
  624. package/dist/ui/frameOverlay.d.ts +59 -0
  625. package/dist/ui/frameOverlay.js +76 -0
  626. package/dist/ui/framePacer.d.ts +142 -0
  627. package/dist/ui/framePacer.js +284 -0
  628. package/dist/ui/frameRecorder.d.ts +188 -0
  629. package/dist/ui/frameRecorder.js +221 -0
  630. package/dist/ui/fullscreen.d.ts +72 -0
  631. package/dist/ui/fullscreen.js +92 -0
  632. package/dist/ui/pixelCursor.d.ts +28 -0
  633. package/dist/ui/pixelCursor.js +66 -0
  634. package/dist/ui/splash.d.ts +86 -0
  635. package/dist/ui/splash.js +300 -0
  636. package/dist/ui/stillFrame.d.ts +22 -0
  637. package/dist/ui/stillFrame.js +40 -0
  638. package/package.json +60 -0
  639. package/src/behavior/behaviorTree.ts +346 -0
  640. package/src/build/shaderComments.ts +0 -0
  641. package/src/cinematic/player.ts +209 -0
  642. package/src/cinematic/script.ts +231 -0
  643. package/src/core/bootGate.ts +49 -0
  644. package/src/core/loadTracker.ts +144 -0
  645. package/src/core/loop.ts +211 -0
  646. package/src/core/messageQueue.ts +145 -0
  647. package/src/core/onceSet.ts +65 -0
  648. package/src/core/preferences.ts +177 -0
  649. package/src/core/remoteSave.ts +347 -0
  650. package/src/core/rng.ts +101 -0
  651. package/src/core/stepBudget.ts +136 -0
  652. package/src/core/storage.ts +114 -0
  653. package/src/core/tickTrace.ts +129 -0
  654. package/src/core/timeCurve.ts +175 -0
  655. package/src/core/withDefaults.ts +28 -0
  656. package/src/dev/contactProbe.ts +115 -0
  657. package/src/dev/fpsMeter.ts +101 -0
  658. package/src/environment/celestialClock.ts +385 -0
  659. package/src/geometry/decal.ts +260 -0
  660. package/src/geometry/filmPatch.ts +158 -0
  661. package/src/geometry/growable.ts +165 -0
  662. package/src/geometry/lightVolume.ts +161 -0
  663. package/src/geometry/meshBuilder.ts +1397 -0
  664. package/src/geometry/pixelFont.ts +189 -0
  665. package/src/geometry/ribbon.ts +790 -0
  666. package/src/geometry/shellFill.ts +265 -0
  667. package/src/geometry/spline.ts +659 -0
  668. package/src/geometry/tangents.ts +173 -0
  669. package/src/geometry/textMesh.ts +96 -0
  670. package/src/geometry/treeBuilder.ts +114 -0
  671. package/src/host/display.ts +180 -0
  672. package/src/host/files.ts +77 -0
  673. package/src/host/lifecycle.ts +63 -0
  674. package/src/host/platformServices.ts +23 -0
  675. package/src/host/screen.ts +134 -0
  676. package/src/index.ts +730 -0
  677. package/src/input/actionMap.ts +377 -0
  678. package/src/input/activeDevice.ts +86 -0
  679. package/src/input/gamepadIdentity.ts +131 -0
  680. package/src/input/gamepadMapping.ts +121 -0
  681. package/src/input/input.ts +872 -0
  682. package/src/input/inputHarness.ts +226 -0
  683. package/src/input/rightGesture.ts +76 -0
  684. package/src/input/rumble.ts +138 -0
  685. package/src/input/touchControls.ts +451 -0
  686. package/src/input/typingTarget.ts +26 -0
  687. package/src/math/bounds.ts +153 -0
  688. package/src/math/color.ts +37 -0
  689. package/src/math/exact.ts +274 -0
  690. package/src/math/frustum.ts +115 -0
  691. package/src/math/intersect.ts +228 -0
  692. package/src/math/scalar.ts +109 -0
  693. package/src/math/spring.ts +106 -0
  694. package/src/nav/navGraph.ts +168 -0
  695. package/src/nav/navPath.ts +217 -0
  696. package/src/nav/navSearch.ts +200 -0
  697. package/src/physics/boxSurface.ts +96 -0
  698. package/src/physics/colliderSurface.ts +213 -0
  699. package/src/physics/compositeSurface.ts +111 -0
  700. package/src/physics/heightSurface.ts +234 -0
  701. package/src/physics/ribbonSurface.ts +642 -0
  702. package/src/render/ambientOcclusionPass.ts +200 -0
  703. package/src/render/areaLights.ts +194 -0
  704. package/src/render/areaShadowSet.ts +405 -0
  705. package/src/render/atmosphere.ts +186 -0
  706. package/src/render/backend/api.ts +802 -0
  707. package/src/render/backend/budget.ts +139 -0
  708. package/src/render/backend/createRenderer.ts +343 -0
  709. package/src/render/backend/deadline.ts +37 -0
  710. package/src/render/backend/probe.ts +152 -0
  711. package/src/render/backend/select.ts +288 -0
  712. package/src/render/backend/timer.ts +59 -0
  713. package/src/render/backend/webgl2/instanced.ts +46 -0
  714. package/src/render/backend/webgl2/morphTexture.ts +58 -0
  715. package/src/render/backend/webgl2/renderer.ts +7484 -0
  716. package/src/render/backend/webgl2/skinPaletteTexture.ts +70 -0
  717. package/src/render/backend/webgpu/boltPass.ts +205 -0
  718. package/src/render/backend/webgpu/buffers.ts +553 -0
  719. package/src/render/backend/webgpu/causticsPass.ts +174 -0
  720. package/src/render/backend/webgpu/clusterBinner.ts +259 -0
  721. package/src/render/backend/webgpu/depthPass.ts +261 -0
  722. package/src/render/backend/webgpu/device.ts +148 -0
  723. package/src/render/backend/webgpu/drawCommand.ts +79 -0
  724. package/src/render/backend/webgpu/environmentTexels.ts +96 -0
  725. package/src/render/backend/webgpu/filmPass.ts +123 -0
  726. package/src/render/backend/webgpu/flatPass.ts +635 -0
  727. package/src/render/backend/webgpu/flockPass.ts +163 -0
  728. package/src/render/backend/webgpu/gpuTimestamps.ts +255 -0
  729. package/src/render/backend/webgpu/insetPass.ts +123 -0
  730. package/src/render/backend/webgpu/instanced.ts +55 -0
  731. package/src/render/backend/webgpu/lightVolumePass.ts +203 -0
  732. package/src/render/backend/webgpu/linePass.ts +270 -0
  733. package/src/render/backend/webgpu/morphTexture.ts +61 -0
  734. package/src/render/backend/webgpu/panelPass.ts +138 -0
  735. package/src/render/backend/webgpu/particlePass.ts +370 -0
  736. package/src/render/backend/webgpu/pipelineCache.ts +152 -0
  737. package/src/render/backend/webgpu/plumePass.ts +292 -0
  738. package/src/render/backend/webgpu/pointShadowPass.ts +368 -0
  739. package/src/render/backend/webgpu/postPass.ts +470 -0
  740. package/src/render/backend/webgpu/probePass.ts +218 -0
  741. package/src/render/backend/webgpu/reflectionPass.ts +137 -0
  742. package/src/render/backend/webgpu/renderer.ts +13004 -0
  743. package/src/render/backend/webgpu/scatterPass.ts +354 -0
  744. package/src/render/backend/webgpu/sdfTextPass.ts +220 -0
  745. package/src/render/backend/webgpu/shaderModules.ts +49 -0
  746. package/src/render/backend/webgpu/shaders/clusterBin.wgsl.ts +219 -0
  747. package/src/render/backend/webgpu/skinPaletteRing.ts +105 -0
  748. package/src/render/backend/webgpu/skinPaletteTexture.ts +79 -0
  749. package/src/render/backend/webgpu/skyPass.ts +85 -0
  750. package/src/render/backend/webgpu/surfaceTexturePass.ts +257 -0
  751. package/src/render/backend/webgpu/textPass.ts +224 -0
  752. package/src/render/backend/webgpu/uniformRing.ts +228 -0
  753. package/src/render/backend/webgpu/waterPass.ts +211 -0
  754. package/src/render/backend/webgpu/windStreakPass.ts +196 -0
  755. package/src/render/billboard.ts +108 -0
  756. package/src/render/bloomChain.ts +101 -0
  757. package/src/render/bloomPass.ts +256 -0
  758. package/src/render/boltBatch.ts +141 -0
  759. package/src/render/boltPool.ts +360 -0
  760. package/src/render/boom.ts +123 -0
  761. package/src/render/camera.ts +348 -0
  762. package/src/render/causticsRenderer.ts +171 -0
  763. package/src/render/cinematicCamera.ts +278 -0
  764. package/src/render/clusteredLights.ts +598 -0
  765. package/src/render/colourGrade.ts +109 -0
  766. package/src/render/compute.ts +83 -0
  767. package/src/render/contextLoss.ts +50 -0
  768. package/src/render/debugLines.ts +411 -0
  769. package/src/render/decalPass.ts +124 -0
  770. package/src/render/decalProjector.ts +390 -0
  771. package/src/render/decalQueue.ts +110 -0
  772. package/src/render/depthConvention.ts +383 -0
  773. package/src/render/drawingBuffer.ts +89 -0
  774. package/src/render/emptyTexture.ts +130 -0
  775. package/src/render/envProbeArray.ts +201 -0
  776. package/src/render/equirectToCube.ts +160 -0
  777. package/src/render/flockGeometry.ts +53 -0
  778. package/src/render/flockRenderer.ts +111 -0
  779. package/src/render/frame/arena.ts +93 -0
  780. package/src/render/frame/index.ts +29 -0
  781. package/src/render/frame/replay.ts +50 -0
  782. package/src/render/frame/resources.ts +61 -0
  783. package/src/render/frame/schedule.ts +85 -0
  784. package/src/render/frameView.ts +20 -0
  785. package/src/render/gizmo.ts +923 -0
  786. package/src/render/globalMedium.ts +154 -0
  787. package/src/render/globalMediumPass.ts +323 -0
  788. package/src/render/gpuCapability.ts +244 -0
  789. package/src/render/gpuTimer.ts +182 -0
  790. package/src/render/halfFloat.ts +39 -0
  791. package/src/render/handleRegistry.ts +96 -0
  792. package/src/render/iesProfile.ts +215 -0
  793. package/src/render/imageTexels.ts +109 -0
  794. package/src/render/instancedMesh.ts +187 -0
  795. package/src/render/instances.ts +92 -0
  796. package/src/render/lightBudget.ts +655 -0
  797. package/src/render/lightMatrix.ts +69 -0
  798. package/src/render/lightVolumeDraw.ts +216 -0
  799. package/src/render/lineBatch.ts +180 -0
  800. package/src/render/linePoints.ts +61 -0
  801. package/src/render/livePointShadowSet.ts +285 -0
  802. package/src/render/lod.ts +86 -0
  803. package/src/render/mesh.ts +536 -0
  804. package/src/render/occlusion.ts +406 -0
  805. package/src/render/oitPass.ts +230 -0
  806. package/src/render/orderIndependent.ts +136 -0
  807. package/src/render/particleBatch.ts +299 -0
  808. package/src/render/particleMaterial.ts +84 -0
  809. package/src/render/particlePool.ts +291 -0
  810. package/src/render/pass.ts +203 -0
  811. package/src/render/passTarget.ts +225 -0
  812. package/src/render/pickable.ts +222 -0
  813. package/src/render/planarReflection.ts +233 -0
  814. package/src/render/planarReflectionDraw.ts +87 -0
  815. package/src/render/plumeGeometry.ts +75 -0
  816. package/src/render/plumeMaterial.ts +99 -0
  817. package/src/render/plumeRenderer.ts +272 -0
  818. package/src/render/pointLightSelection.ts +447 -0
  819. package/src/render/pointShadowArray.ts +276 -0
  820. package/src/render/pointShadowBudget.ts +305 -0
  821. package/src/render/pointShadowImage.ts +470 -0
  822. package/src/render/pointShadowMap.ts +228 -0
  823. package/src/render/pointShadowSystem.ts +408 -0
  824. package/src/render/prefilterEnvMap.ts +162 -0
  825. package/src/render/prefilterPass.ts +115 -0
  826. package/src/render/probeGrid.ts +248 -0
  827. package/src/render/rainField.ts +240 -0
  828. package/src/render/reflectionProbe.ts +530 -0
  829. package/src/render/renderQuality.ts +1054 -0
  830. package/src/render/rendererHarness.ts +214 -0
  831. package/src/render/resolutionGovernor.ts +189 -0
  832. package/src/render/scatterDeform.ts +72 -0
  833. package/src/render/sceneTarget.ts +1032 -0
  834. package/src/render/screenSpaceReflection.ts +524 -0
  835. package/src/render/sdfFont.ts +157 -0
  836. package/src/render/sdfTextLayout.ts +206 -0
  837. package/src/render/sdfTextRenderer.ts +233 -0
  838. package/src/render/seaState.ts +55 -0
  839. package/src/render/segmentQuads.ts +92 -0
  840. package/src/render/shader.ts +190 -0
  841. package/src/render/shaders/ambientOcclusion.ts +354 -0
  842. package/src/render/shaders/arcane.ts +103 -0
  843. package/src/render/shaders/bloom.ts +232 -0
  844. package/src/render/shaders/bolt.ts +164 -0
  845. package/src/render/shaders/caustics.ts +169 -0
  846. package/src/render/shaders/conditionals.ts +58 -0
  847. package/src/render/shaders/decalProject.ts +127 -0
  848. package/src/render/shaders/depth.ts +131 -0
  849. package/src/render/shaders/depthOfField.ts +141 -0
  850. package/src/render/shaders/film.ts +285 -0
  851. package/src/render/shaders/fire.ts +180 -0
  852. package/src/render/shaders/flat/directionalShadow.ts +196 -0
  853. package/src/render/shaders/flat/index.ts +502 -0
  854. package/src/render/shaders/flat/lobes.ts +399 -0
  855. package/src/render/shaders/flat/main.ts +1631 -0
  856. package/src/render/shaders/flat/pointShadow.ts +323 -0
  857. package/src/render/shaders/flat/preamble.ts +875 -0
  858. package/src/render/shaders/flat/probeGrid.ts +152 -0
  859. package/src/render/shaders/flat/surface.ts +41 -0
  860. package/src/render/shaders/flock.ts +103 -0
  861. package/src/render/shaders/fog.ts +99 -0
  862. package/src/render/shaders/fullscreen.ts +28 -0
  863. package/src/render/shaders/generated/ambientOcclusion.wgsl.ts +74 -0
  864. package/src/render/shaders/generated/arcane.wgsl.ts +98 -0
  865. package/src/render/shaders/generated/bloom.wgsl.ts +79 -0
  866. package/src/render/shaders/generated/bolt.wgsl.ts +127 -0
  867. package/src/render/shaders/generated/caustics.wgsl.ts +127 -0
  868. package/src/render/shaders/generated/decalProject.wgsl.ts +69 -0
  869. package/src/render/shaders/generated/depth.wgsl.ts +142 -0
  870. package/src/render/shaders/generated/film.wgsl.ts +143 -0
  871. package/src/render/shaders/generated/fire.wgsl.ts +137 -0
  872. package/src/render/shaders/generated/flat.wgsl.ts +9195 -0
  873. package/src/render/shaders/generated/flock.wgsl.ts +82 -0
  874. package/src/render/shaders/generated/fullscreen.wgsl.ts +20 -0
  875. package/src/render/shaders/generated/globalMedium.wgsl.ts +139 -0
  876. package/src/render/shaders/generated/lightVolume.wgsl.ts +242 -0
  877. package/src/render/shaders/generated/line.wgsl.ts +137 -0
  878. package/src/render/shaders/generated/octahedralResolve.wgsl.ts +60 -0
  879. package/src/render/shaders/generated/oitResolve.wgsl.ts +31 -0
  880. package/src/render/shaders/generated/panel.wgsl.ts +57 -0
  881. package/src/render/shaders/generated/particle.wgsl.ts +343 -0
  882. package/src/render/shaders/generated/prefilter.wgsl.ts +69 -0
  883. package/src/render/shaders/generated/rush.wgsl.ts +139 -0
  884. package/src/render/shaders/generated/scatter.wgsl.ts +205 -0
  885. package/src/render/shaders/generated/sdfText.wgsl.ts +78 -0
  886. package/src/render/shaders/generated/sky.wgsl.ts +109 -0
  887. package/src/render/shaders/generated/smoke.wgsl.ts +93 -0
  888. package/src/render/shaders/generated/ssrResolve.wgsl.ts +26 -0
  889. package/src/render/shaders/generated/ssrTrace.wgsl.ts +94 -0
  890. package/src/render/shaders/generated/temporalResolve.wgsl.ts +54 -0
  891. package/src/render/shaders/generated/text.wgsl.ts +107 -0
  892. package/src/render/shaders/generated/water.wgsl.ts +256 -0
  893. package/src/render/shaders/generated/windStreaks.wgsl.ts +82 -0
  894. package/src/render/shaders/gerstner.ts +110 -0
  895. package/src/render/shaders/globalMedium.ts +396 -0
  896. package/src/render/shaders/lightVolume.ts +546 -0
  897. package/src/render/shaders/line.ts +125 -0
  898. package/src/render/shaders/morph.ts +62 -0
  899. package/src/render/shaders/octahedral.ts +170 -0
  900. package/src/render/shaders/octahedralResolve.ts +83 -0
  901. package/src/render/shaders/oitResolve.ts +42 -0
  902. package/src/render/shaders/outputTransform.ts +107 -0
  903. package/src/render/shaders/panel.ts +51 -0
  904. package/src/render/shaders/particle.ts +438 -0
  905. package/src/render/shaders/prefilter.ts +278 -0
  906. package/src/render/shaders/rush.ts +357 -0
  907. package/src/render/shaders/scatter.ts +211 -0
  908. package/src/render/shaders/sdfText.ts +68 -0
  909. package/src/render/shaders/skinning.ts +59 -0
  910. package/src/render/shaders/sky.ts +243 -0
  911. package/src/render/shaders/smoke.ts +89 -0
  912. package/src/render/shaders/ssrResolve.ts +28 -0
  913. package/src/render/shaders/ssrTrace.ts +233 -0
  914. package/src/render/shaders/tangentFrame.ts +70 -0
  915. package/src/render/shaders/temporalResolve.ts +151 -0
  916. package/src/render/shaders/text.ts +156 -0
  917. package/src/render/shaders/vertexChannel.ts +71 -0
  918. package/src/render/shaders/water.ts +356 -0
  919. package/src/render/shaders/windStreaks.ts +130 -0
  920. package/src/render/shadowCasters.ts +124 -0
  921. package/src/render/shadowMap.ts +89 -0
  922. package/src/render/shotPlacement.ts +266 -0
  923. package/src/render/skinPalette.ts +157 -0
  924. package/src/render/ssrPass.ts +241 -0
  925. package/src/render/surfaceSheet.ts +158 -0
  926. package/src/render/surfaceTexture.ts +310 -0
  927. package/src/render/temporalAa.ts +221 -0
  928. package/src/render/temporalPass.ts +153 -0
  929. package/src/render/textLayout.ts +239 -0
  930. package/src/render/textRenderer.ts +201 -0
  931. package/src/render/thirdPersonCamera.ts +578 -0
  932. package/src/render/trample.ts +140 -0
  933. package/src/render/translucentQueue.ts +161 -0
  934. package/src/render/uniformVectorBudget.ts +246 -0
  935. package/src/render/uploadStep.ts +14 -0
  936. package/src/render/vertexDefaults.ts +115 -0
  937. package/src/render/visibility.ts +56 -0
  938. package/src/render/waterDraw.ts +167 -0
  939. package/src/render/waterGrid.ts +180 -0
  940. package/src/render/waterRenderer.ts +352 -0
  941. package/src/render/wind.ts +79 -0
  942. package/src/render/windField.ts +50 -0
  943. package/src/render/windStreakDraw.ts +131 -0
  944. package/src/render/windStreakRenderer.ts +130 -0
  945. package/src/scene/node.ts +336 -0
  946. package/src/scene/visit.ts +82 -0
  947. package/src/ui/clipMime.ts +109 -0
  948. package/src/ui/exportTarget.ts +491 -0
  949. package/src/ui/frameOverlay.ts +159 -0
  950. package/src/ui/framePacer.ts +351 -0
  951. package/src/ui/frameRecorder.ts +333 -0
  952. package/src/ui/fullscreen.ts +113 -0
  953. package/src/ui/pixelCursor.ts +99 -0
  954. package/src/ui/splash.ts +347 -0
  955. package/src/ui/stillFrame.ts +44 -0
@@ -0,0 +1,1184 @@
1
+ import { GrowableF32, GrowableU32 } from './growable.js';
2
+ /**
3
+ * The emissive colour of a vertex that has not been given one: negative, which no real
4
+ * colour can be, so "inherit the surface's albedo" is representable without a second
5
+ * attribute or a flag. Black would have been ambiguous — a surface can legitimately want
6
+ * to emit nothing — and this cannot be mistaken for anything.
7
+ */
8
+ const INHERIT_ALBEDO = -1;
9
+ /** Same idea for roughness: negative means "whatever the engine defaults to". */
10
+ const INHERIT_ROUGHNESS = -1;
11
+ /**
12
+ * CPU-side accumulator that merges axis-aligned boxes into one static mesh —
13
+ * the entire grey-box world becomes a single draw call.
14
+ *
15
+ * Faces are generated per-axis with cyclic tangents (a,b,c) so that U×V = N,
16
+ * guaranteeing outward CCW winding for back-face culling.
17
+ *
18
+ * **Long on purpose, and this was measured rather than assumed** (2026-08-24). The file is 1,056
19
+ * lines and the guideline is around 600, so it was examined for a seam and has none worth taking:
20
+ * it is a fluent accumulator whose every method returns `this` and reads class state — 146 reads
21
+ * of `this` across the class — and the genuinely pure code in it comes to 44 lines at the tail
22
+ * (`assertOrthonormal`, `dot`, and the three constants they are made of). Extracting 44 lines
23
+ * would leave the class exactly as long and add a file to follow.
24
+ *
25
+ * Carving the class itself is the alternative and it is worse: `addBox`, `addCapsule`, `addBlob`
26
+ * and `addCylinder` all end in the same `addFace` and `addVertex`, so any cut runs straight
27
+ * through the shared tail and produces modules that only make sense read together. An invented
28
+ * seam costs more readability than the length it relieved, which is the rule
29
+ * the render-graph design states — and this file is the
30
+ * case that rule was written about, named in it by name.
31
+ *
32
+ * **What would make this wrong:** a second builder wanting the same face generation, which would
33
+ * turn the shared tail from an internal detail into an interface. Nothing wants it today.
34
+ */
35
+ export class MeshBuilder {
36
+ positions = new GrowableF32();
37
+ normals = new GrowableF32();
38
+ colors = new GrowableF32();
39
+ emissive = new GrowableF32();
40
+ /**
41
+ * Per-vertex specular, accumulated always and emitted only when something asked
42
+ * for it — see `build`. Keeping the array unconditionally costs one push per
43
+ * vertex at build time and keeps every code path below free of a branch.
44
+ */
45
+ specular = new GrowableF32(256, 0);
46
+ /**
47
+ * Per-vertex emissive *colour*, or the sentinel meaning "whatever this surface is made
48
+ * of". Accumulated always and emitted only when something asked for one, exactly as
49
+ * `specular` is.
50
+ */
51
+ emissiveColor = new GrowableF32(256, INHERIT_ALBEDO);
52
+ /** What `setEmissiveColor` last named, applied to every vertex added after it. */
53
+ emissiveColorState = null;
54
+ /** Per-vertex roughness, and what `setRoughness` last named. */
55
+ roughness = new GrowableF32(256, INHERIT_ROUGHNESS);
56
+ roughnessState = null;
57
+ /**
58
+ * Per-vertex grain, and what `setGrain` last named. Zero until something asks otherwise,
59
+ * because a surface that never says it is mineral is not mineral.
60
+ */
61
+ grain = new GrowableF32(256, 0);
62
+ grainState = 0;
63
+ /**
64
+ * Per-vertex relief, and what `setRelief` last named. Zero until something asks, because a
65
+ * surface that never says it has texture is smooth.
66
+ */
67
+ relief = new GrowableF32(256, 0);
68
+ reliefState = 0;
69
+ /**
70
+ * Texture coordinates from anything that has real ones, and empty for geometry that does not.
71
+ *
72
+ * Two sources fill this and they are the same kind of thing: a merged mesh's own coordinates,
73
+ * authored against its own maps, and `addBlob`'s surface parameters, which the primitive walks
74
+ * to place every vertex and used to discard.
75
+ *
76
+ * Separate from `planarUvs()`, which *derives* coordinates from position at build time for a
77
+ * wall or a floor. That derivation is a box projection, and on anything round it is three
78
+ * projections meeting at seams with the poles smeared along an axis, which is why a sphere
79
+ * needs its own and cannot borrow them.
80
+ */
81
+ uvs = new GrowableF32();
82
+ indices = new GrowableU32();
83
+ vertexCount = 0;
84
+ /**
85
+ * Where each rigid joint binding starts, as a vertex index, and which joint it binds to.
86
+ *
87
+ * **Spans expanded at build, not four floats pushed at every vertex site.** Seven separate
88
+ * places in this file push a colour and an emissive, and a binding pushed beside each of them
89
+ * is seven chances for a geometry verb added later to forget one — whose symptom is a short
90
+ * attribute buffer, which some drivers read past the end as zeroes and others answer by
91
+ * dropping the draw, neither raising a GL error. `paddedUvs` already solved the same problem
92
+ * the same way.
93
+ *
94
+ * **What it costs** is that a binding is resolved once per span rather than per vertex, which
95
+ * is cheaper. **What would make it wrong** is a caller wanting two influences on one vertex:
96
+ * this cannot express it, and a rigged import gives them that instead.
97
+ */
98
+ jointSpans = [];
99
+ /** Whether anything was ever bound, so `build` knows to emit at all. */
100
+ anyJoint = false;
101
+ /**
102
+ * Merge an existing mesh in at a position and uniform scale.
103
+ *
104
+ * For geometry that is built once and then stamped around the world — a tree
105
+ * trunk, say. Merging keeps it inside the single static draw call rather than
106
+ * adding one per copy, which is the whole reason the world is one mesh.
107
+ *
108
+ * Uniform scale only: a non-uniform one would need normals transformed by the
109
+ * inverse transpose, and nothing here has wanted that yet.
110
+ */
111
+ addMesh(mesh, x, y, z, scale = 1) {
112
+ const base = this.vertexCount;
113
+ const count = mesh.positions.length / 3;
114
+ for (let i = 0; i < count; i++) {
115
+ this.positions.push((mesh.positions[i * 3] ?? 0) * scale + x, (mesh.positions[i * 3 + 1] ?? 0) * scale + y, (mesh.positions[i * 3 + 2] ?? 0) * scale + z);
116
+ this.normals.push(mesh.normals[i * 3] ?? 0, mesh.normals[i * 3 + 1] ?? 0, mesh.normals[i * 3 + 2] ?? 0);
117
+ this.colors.push(mesh.colors[i * 3] ?? 0, mesh.colors[i * 3 + 1] ?? 0, mesh.colors[i * 3 + 2] ?? 0);
118
+ this.emissive.push(mesh.emissive[i] ?? 0);
119
+ // Absent on almost every mesh, which is what `?? 0` is for: merging must not
120
+ // invent a shine, and must not drop one the source had.
121
+ this.specular.push(mesh.specular?.[i] ?? 0);
122
+ this.emissiveColor.push(mesh.emissiveColor?.[i * 3] ?? INHERIT_ALBEDO, mesh.emissiveColor?.[i * 3 + 1] ?? INHERIT_ALBEDO, mesh.emissiveColor?.[i * 3 + 2] ?? INHERIT_ALBEDO);
123
+ /*
124
+ * The merged mesh's own roughness, falling back to inherit — and its absence here
125
+ * was the defect. Every other attribute was carried across and this one was not, so
126
+ * merging a mesh left `roughness` shorter than the vertex count by exactly the
127
+ * number of vertices merged. A short attribute buffer is not benign: a driver may
128
+ * read zeroes, and it may drop the draw outright, which is what it did.
129
+ */
130
+ this.roughness.push(mesh.roughness?.[i] ?? this.roughnessState ?? INHERIT_ROUGHNESS);
131
+ // The merged mesh's own grain, and the builder's current state for one that states none.
132
+ this.grain.push(mesh.grain?.[i] ?? this.grainState);
133
+ this.relief.push(mesh.relief?.[i] ?? this.reliefState);
134
+ }
135
+ this.mergeUvs(mesh, base, count);
136
+ for (const index of mesh.indices)
137
+ this.indices.push(base + index);
138
+ this.vertexCount += count;
139
+ return this;
140
+ }
141
+ /**
142
+ * Merge an existing mesh under an orthonormal basis: rotated, then placed.
143
+ *
144
+ * `addMesh` translates and scales, which is enough for anything whose own
145
+ * axes are the world's — a tree, a rock. It is not enough for anything that
146
+ * has to *agree with a surface*: a marking on a banked deck, a sign facing
147
+ * back along a curve, a letter standing on a slope. Until now the only way to
148
+ * follow a surface was to be a ribbon built from the same curve, which works
149
+ * beautifully for bands across a deck and cannot draw a glyph.
150
+ *
151
+ * The basis is assumed orthonormal and is checked, because that assumption is
152
+ * what lets normals be rotated by the same three vectors as the positions: a
153
+ * non-uniform or skewed basis needs the inverse transpose, and silently
154
+ * getting that wrong produces geometry that is lit as if it were facing
155
+ * somewhere else — which looks like a shading bug, not a maths one.
156
+ *
157
+ * @param scale uniform, applied in the local frame before the basis.
158
+ */
159
+ addOrientedMesh(mesh, origin, right, up, forward, scale = 1) {
160
+ assertOrthonormal(right, up, forward);
161
+ const base = this.vertexCount;
162
+ const count = mesh.positions.length / 3;
163
+ for (let i = 0; i < count; i++) {
164
+ const px = (mesh.positions[i * 3] ?? 0) * scale;
165
+ const py = (mesh.positions[i * 3 + 1] ?? 0) * scale;
166
+ const pz = (mesh.positions[i * 3 + 2] ?? 0) * scale;
167
+ this.positions.push(origin[0] + right[0] * px + up[0] * py + forward[0] * pz, origin[1] + right[1] * px + up[1] * py + forward[1] * pz, origin[2] + right[2] * px + up[2] * py + forward[2] * pz);
168
+ const nx = mesh.normals[i * 3] ?? 0;
169
+ const ny = mesh.normals[i * 3 + 1] ?? 0;
170
+ const nz = mesh.normals[i * 3 + 2] ?? 0;
171
+ this.normals.push(right[0] * nx + up[0] * ny + forward[0] * nz, right[1] * nx + up[1] * ny + forward[1] * nz, right[2] * nx + up[2] * ny + forward[2] * nz);
172
+ this.colors.push(mesh.colors[i * 3] ?? 0, mesh.colors[i * 3 + 1] ?? 0, mesh.colors[i * 3 + 2] ?? 0);
173
+ this.emissive.push(mesh.emissive[i] ?? 0);
174
+ // Absent on almost every mesh, which is what `?? 0` is for: merging must not
175
+ // invent a shine, and must not drop one the source had.
176
+ this.specular.push(mesh.specular?.[i] ?? 0);
177
+ this.emissiveColor.push(mesh.emissiveColor?.[i * 3] ?? INHERIT_ALBEDO, mesh.emissiveColor?.[i * 3 + 1] ?? INHERIT_ALBEDO, mesh.emissiveColor?.[i * 3 + 2] ?? INHERIT_ALBEDO);
178
+ /*
179
+ * The merged mesh's own roughness, falling back to inherit — and its absence here
180
+ * was the defect. Every other attribute was carried across and this one was not, so
181
+ * merging a mesh left `roughness` shorter than the vertex count by exactly the
182
+ * number of vertices merged. A short attribute buffer is not benign: a driver may
183
+ * read zeroes, and it may drop the draw outright, which is what it did.
184
+ */
185
+ this.roughness.push(mesh.roughness?.[i] ?? this.roughnessState ?? INHERIT_ROUGHNESS);
186
+ // The merged mesh's own grain, and the builder's current state for one that states none.
187
+ this.grain.push(mesh.grain?.[i] ?? this.grainState);
188
+ this.relief.push(mesh.relief?.[i] ?? this.reliefState);
189
+ }
190
+ this.mergeUvs(mesh, base, count);
191
+ for (const index of mesh.indices)
192
+ this.indices.push(base + index);
193
+ this.vertexCount += count;
194
+ return this;
195
+ }
196
+ /**
197
+ * @param specular 0–1: how sharply this box takes a sun highlight. Zero, and
198
+ * therefore free, for everything that is not meant to shine.
199
+ */
200
+ /**
201
+ * What colour the following geometry emits, or `null` for its own albedo.
202
+ *
203
+ * State rather than a parameter, because `emissive` already reaches a dozen signatures
204
+ * and a thirteenth argument on each of them is where a builder stops being readable.
205
+ * It is also the honest shape: an emissive colour belongs to a *material*, and a
206
+ * material covers many calls.
207
+ *
208
+ * The default — `null` — is the behaviour this builder has always had: the emissive term
209
+ * is the surface's own colour scaled by its emissive amount, which is right for anything
210
+ * that glows because it is hot or lit from within. It is wrong for a surface whose glow
211
+ * is a different colour from its paint, and that is not a corner case: a ceiling tile
212
+ * emitting a dull warm haze over a pale panel is how a strip-lit interior reads, and
213
+ * approximating it by scaling the panel's own colour gets the brightness right and the
214
+ * hue wrong every time.
215
+ */
216
+ /**
217
+ * How rough the following geometry is, 0–1, or `null` for the engine's default.
218
+ *
219
+ * State for the same reason `setEmissiveColor` is, and it answers a different question
220
+ * from `specular`: that one is how much light comes back, this is over how wide an angle.
221
+ * A polished floor and a matte one can return the same energy and look nothing alike.
222
+ */
223
+ /**
224
+ * Bind every vertex added from here on to one joint, at full weight, or `null` to stop.
225
+ *
226
+ * Rigid binding: one influence. A mesh assembled from solid pieces — a rig of boxes, a
227
+ * chandelier, a tank's wheels — needs exactly this, and smooth skinning across a joint is a
228
+ * different job belonging to a baker that reads a rigged source.
229
+ *
230
+ * Stopping binds what follows to joint 0 rather than to nothing, for the reason
231
+ * `expandedJoints` gives: a zero-weight vertex collapses onto the origin.
232
+ */
233
+ setJoint(index) {
234
+ if (index !== null && (!Number.isInteger(index) || index < 0)) {
235
+ throw new Error(`MeshBuilder: a joint index must be a non-negative integer, got ${index}`);
236
+ }
237
+ if (index !== null)
238
+ this.anyJoint = true;
239
+ this.jointSpans.push({ from: this.positions.length / 3, joint: index ?? 0 });
240
+ return this;
241
+ }
242
+ setRoughness(roughness) {
243
+ this.roughnessState = roughness;
244
+ return this;
245
+ }
246
+ /**
247
+ * How much visible mineral structure the following geometry has, 0–1.
248
+ *
249
+ * State beside `setRoughness`, and deliberately **independent** of it. Roughness is how
250
+ * widely a surface scatters a highlight; grain is whether it has structure you can see
251
+ * at arm's length. Painted plaster is rough with no grain and polished granite is smooth
252
+ * with a great deal, so neither can be derived from the other — which is exactly what the
253
+ * two previous versions of this feature tried, first through `specular` and then through
254
+ * `roughness`, and both were wrong in both directions.
255
+ *
256
+ * **Zero is the default and means none.** A scene says which of its surfaces are stone
257
+ * while it is building them, and everything it does not mention stays smooth.
258
+ */
259
+ setGrain(grain) {
260
+ this.grainState = Math.min(1, Math.max(0, grain));
261
+ return this;
262
+ }
263
+ /**
264
+ * How much microscopic relief the surfaces built after this have, 0 to 1.
265
+ *
266
+ * **What it is for.** A road is not flat and neither is cast concrete, a plaster wall or a
267
+ * hammered tray: each is covered in structure far too small to model and far too large to
268
+ * ignore, and it is what makes them read as material rather than as coloured planes. This is
269
+ * the general capability for that, so a scene states an amount here and the *material* decides
270
+ * how coarse it is and how deep, through `Renderer.setSurfaceRelief`.
271
+ *
272
+ * **Distinct from `setGrain`, and both may apply.** Grain varies how much light a point takes,
273
+ * so it mottles a face that stays flat. Relief varies which way the point faces, so it catches
274
+ * a light from one side and shades on the other, and it survives being seen from a shallow
275
+ * angle where a brightness mottle washes out. Stone wants both. Asphalt wants mostly this.
276
+ *
277
+ * **Zero is the default and means none**, so every surface a scene does not mention is smooth
278
+ * and no world built before this existed moves.
279
+ */
280
+ setRelief(relief) {
281
+ this.reliefState = Math.min(1, Math.max(0, relief));
282
+ return this;
283
+ }
284
+ setEmissiveColor(color) {
285
+ this.emissiveColorState = color;
286
+ return this;
287
+ }
288
+ addBox(center, halfExtents, color, emissive = 0, specular = 0) {
289
+ for (let axis = 0; axis < 3; axis++) {
290
+ for (const sign of SIGNS) {
291
+ this.addFace(center, halfExtents, color, emissive, axis, sign, specular);
292
+ }
293
+ }
294
+ return this;
295
+ }
296
+ /**
297
+ * Add a single quad from four corners wound counter-clockwise as seen from the front.
298
+ *
299
+ * The escape hatch from `addBox` for geometry that is a *surface* rather than a solid.
300
+ * Boxing every solid cell of a grid world emits the faces between two adjacent solids
301
+ * as well — buried, invisible, and coplanar with each other, so at the edge where they
302
+ * meet open space they z-fight into thin lines along every cell boundary. Reported as
303
+ * walls that look like "blocks glued together". With this, a caller emits only the
304
+ * faces that border something you can stand in, which removes the interior pairs
305
+ * entirely and costs far fewer vertices than the boxes did.
306
+ *
307
+ * The normal is derived from the winding rather than taken as a parameter, so a quad
308
+ * cannot be lit as if it faced a direction it does not.
309
+ */
310
+ /**
311
+ * A flat quad, whose normal comes from `b - a` crossed with **`d - a`** rather than `c - a`.
312
+ *
313
+ * **That is not what anybody assumes, and it decides which side the face lights from.** Every
314
+ * other winding convention in graphics walks consecutive vertices, so a horizontal quad wound
315
+ * the obvious way round, `(x0,z0) (x1,z0) (x1,z1) (x0,z1)`, gives `+X` crossed with `+Z`,
316
+ * which points at the floor. Reported from outside after every ground in an application faced
317
+ * downward for four scenarios: they were lit by lamps near the floor, where a flipped normal
318
+ * is a dimmer surface rather than an absent one, and it only became visible on the fifth world
319
+ * when a directional lit it and the ground took nothing from the sun at all.
320
+ *
321
+ * Reverse the last two corners to flip it, or wind anticlockwise seen from the side the face
322
+ * should light from.
323
+ *
324
+ * The normal is per *face*, so anything assembled from these is flat-shaded by construction.
325
+ * A surface that has to be both curved and varied wants `addBlob`.
326
+ */
327
+ addQuad(a, b, c, d, color, emissive = 0, specular = 0) {
328
+ const ux = b[0] - a[0];
329
+ const uy = b[1] - a[1];
330
+ const uz = b[2] - a[2];
331
+ const vx = d[0] - a[0];
332
+ const vy = d[1] - a[1];
333
+ const vz = d[2] - a[2];
334
+ let nx = uy * vz - uz * vy;
335
+ let ny = uz * vx - ux * vz;
336
+ let nz = ux * vy - uy * vx;
337
+ const length = Math.hypot(nx, ny, nz);
338
+ if (length === 0)
339
+ throw new Error('addQuad: degenerate corners have no normal');
340
+ nx /= length;
341
+ ny /= length;
342
+ nz /= length;
343
+ const base = this.vertexCount;
344
+ for (const corner of [a, b, c, d]) {
345
+ this.positions.push(corner[0], corner[1], corner[2]);
346
+ this.normals.push(nx, ny, nz);
347
+ this.colors.push(color[0], color[1], color[2]);
348
+ this.emissive.push(emissive);
349
+ this.specular.push(specular);
350
+ this.pushEmissiveColor();
351
+ this.roughness.push(this.roughnessState ?? INHERIT_ROUGHNESS);
352
+ this.grain.push(this.grainState);
353
+ this.relief.push(this.reliefState);
354
+ }
355
+ this.indices.push(base, base + 1, base + 2, base, base + 2, base + 3);
356
+ this.vertexCount += 4;
357
+ return this;
358
+ }
359
+ /**
360
+ * A quad that faces **up**, whatever order its corners arrive in.
361
+ *
362
+ * **`addQuad`'s winding rule is the single most expensive trap in this class**, and its own note
363
+ * says why: the normal is `(b − a) × (d − a)`, which is not the order a person walks around a
364
+ * rectangle. Reading that note is not the same as remembering it at four in the morning. It cost
365
+ * five scenes in one application before anybody saw it, and then four separate bugs in one
366
+ * afternoon in another — one of which was every building in a village lit inside out, because a
367
+ * wall is a quad too.
368
+ *
369
+ * So: a ground, a floor, a road, a tabletop, a roof plane seen from above. Pass the corners in
370
+ * whichever order reads naturally and the face lights from the sky. What it costs is one cross
371
+ * product and one comparison, at build time, once.
372
+ *
373
+ * A quad standing exactly vertical has no up and is refused rather than guessed at — a wall
374
+ * passed to this by mistake is a caller error worth hearing about, and `addWallQuad` is the one
375
+ * that wanted it.
376
+ */
377
+ addGroundQuad(a, b, c, d, color, emissive = 0, specular = 0) {
378
+ const up = quadNormal(a, b, d)[1];
379
+ if (up === 0) {
380
+ throw new Error('addGroundQuad: a vertical quad has no upward side; use addWallQuad');
381
+ }
382
+ return up > 0
383
+ ? this.addQuad(a, b, c, d, color, emissive, specular)
384
+ : this.addQuad(a, d, c, b, color, emissive, specular);
385
+ }
386
+ /**
387
+ * A quad that faces **away from a point**, whatever order its corners arrive in.
388
+ *
389
+ * The other nine-tenths of what a consumer wants from `addQuad`: the outside of a wall, of a
390
+ * chimney, of a crate, of anything hollow built from surfaces. `awayFrom` is usually the centre
391
+ * of the room or the solid the face belongs to, and it need not be exact — only on the correct
392
+ * side, which is a thing a caller knows without thinking. That is the whole difference from
393
+ * winding, which is a thing a caller has to look up.
394
+ *
395
+ * A face whose centre *is* the reference point has no outward side, and that is refused: it means
396
+ * the two were mixed up, and picking one silently is how a building ends up lighting its interior
397
+ * while the outside takes nothing from the sun.
398
+ */
399
+ addWallQuad(a, b, c, d, awayFrom, color, emissive = 0, specular = 0) {
400
+ const normal = quadNormal(a, b, d);
401
+ const outward = normal[0] * ((a[0] + b[0] + c[0] + d[0]) / 4 - awayFrom[0]) +
402
+ normal[1] * ((a[1] + b[1] + c[1] + d[1]) / 4 - awayFrom[1]) +
403
+ normal[2] * ((a[2] + b[2] + c[2] + d[2]) / 4 - awayFrom[2]);
404
+ if (outward === 0) {
405
+ throw new Error('addWallQuad: the reference point lies in the face, so there is no outward side');
406
+ }
407
+ return outward > 0
408
+ ? this.addQuad(a, b, c, d, color, emissive, specular)
409
+ : this.addQuad(a, d, c, b, color, emissive, specular);
410
+ }
411
+ /**
412
+ * A box turned to lie along a direction, rather than square to the world axes.
413
+ *
414
+ * **`addBox` is world-axis only, and that is visible the moment anything follows a boundary.** A
415
+ * hedge along a property line built from `addBox` is a row of detached slabs each facing a
416
+ * different way; a window sill is square in plan and pokes out of three walls it does not belong
417
+ * to. Both of those shipped in a consumer's world and both were reported by eye, and the wrapper
418
+ * they wrote to fix it is this method.
419
+ *
420
+ * `halfExtents` are measured **along the box's own axes**: `x` across, `y` up, `z` along
421
+ * `forward`. The other two axes are derived from world up, which is what makes this one argument
422
+ * rather than three: a box that also has to tilt or roll wants `addOrientedMesh`, which takes the
423
+ * full basis and is the reason this does not.
424
+ *
425
+ * Every face is emitted through `addWallQuad` against the box's own centre, so the six outward
426
+ * normals are one argument rather than six windings to get right.
427
+ */
428
+ addOrientedBox(center, halfExtents, forward, color, emissive = 0, specular = 0) {
429
+ const length = Math.hypot(forward[0], forward[1], forward[2]);
430
+ if (length === 0)
431
+ throw new Error('addOrientedBox: forward has no direction');
432
+ const fx = forward[0] / length;
433
+ const fy = forward[1] / length;
434
+ const fz = forward[2] / length;
435
+ /* right = worldUp × forward, which is x = y × z in a right-handed basis: (0,1,0) × f is
436
+ (fz, 0, −fx). Getting this backwards costs nothing visible on a box, which is symmetric
437
+ about all three, and would be a mirrored world the day anything else reads the basis. */
438
+ let rx = fz;
439
+ const ry = 0;
440
+ let rz = -fx;
441
+ const across = Math.hypot(rx, rz);
442
+ if (across === 0) {
443
+ throw new Error('addOrientedBox: forward is vertical, so world up cannot fix the other two axes; use addOrientedMesh');
444
+ }
445
+ rx /= across;
446
+ rz /= across;
447
+ /* up = forward × right, orthonormal by construction because the two above are. */
448
+ const ux = fy * rz - fz * ry;
449
+ const uy = fz * rx - fx * rz;
450
+ const uz = fx * ry - fy * rx;
451
+ const hx = halfExtents[0];
452
+ const hy = halfExtents[1];
453
+ const hz = halfExtents[2];
454
+ const corner = (sx, sy, sz) => [
455
+ center[0] + rx * hx * sx + ux * hy * sy + fx * hz * sz,
456
+ center[1] + ry * hx * sx + uy * hy * sy + fy * hz * sz,
457
+ center[2] + rz * hx * sx + uz * hy * sy + fz * hz * sz,
458
+ ];
459
+ for (const [p, q, r, t] of BOX_FACES) {
460
+ this.addWallQuad(corner(...p), corner(...q), corner(...r), corner(...t), center, color, emissive, specular);
461
+ }
462
+ return this;
463
+ }
464
+ /**
465
+ * A capsule aligned to Y: a cylindrical shaft with a hemispherical cap at each end.
466
+ *
467
+ * The shape almost every organic silhouette is roughed out with — a limb, a torso, a
468
+ * creature seen through fog. A stack of boxes stands in for it badly: the corners read
469
+ * as a *machine* at any distance, which is the wrong instinct entirely for something
470
+ * meant to look alive. Reported as an entity looking "SQUARED".
471
+ *
472
+ * `halfLength` measures the cylindrical part only, so total height is
473
+ * `2 * (halfLength + radius)` and a capsule with `halfLength` 0 is a sphere.
474
+ */
475
+ addCapsule(center, radius, halfLength, color, emissive = 0, segments = 12, rings = 6, specular = 0) {
476
+ if (radius <= 0)
477
+ throw new Error('Capsule radius must be positive.');
478
+ if (!Number.isInteger(segments) || segments < 3) {
479
+ throw new Error('Capsule segments must be an integer of at least 3.');
480
+ }
481
+ if (!Number.isInteger(rings) || rings < 2) {
482
+ throw new Error('Capsule rings must be an integer of at least 2.');
483
+ }
484
+ const base = this.vertexCount;
485
+ /*
486
+ * Two hemispheres of latitude rings, each offset to its own end of the shaft. The
487
+ * bottom cap's last ring and the top cap's first ring are both at the equator radius
488
+ * but at opposite ends, so the quad strip between them *is* the cylinder — the shaft
489
+ * needs no separate geometry and there is no seam to crack.
490
+ */
491
+ const rows = (rings + 1) * 2;
492
+ for (let row = 0; row < rows; row++) {
493
+ const upper = row >= rings + 1;
494
+ const capIndex = upper ? row - (rings + 1) : row;
495
+ const phi = upper
496
+ ? (capIndex / rings) * (Math.PI / 2)
497
+ : (capIndex / rings) * (Math.PI / 2) - Math.PI / 2;
498
+ const cosPhi = Math.cos(phi);
499
+ const sinPhi = Math.sin(phi);
500
+ const y = center[1] + (upper ? halfLength : -halfLength) + sinPhi * radius;
501
+ for (let segment = 0; segment <= segments; segment++) {
502
+ const theta = (segment / segments) * Math.PI * 2;
503
+ const nx = cosPhi * Math.cos(theta);
504
+ const nz = cosPhi * Math.sin(theta);
505
+ this.positions.push(center[0] + nx * radius, y, center[2] + nz * radius);
506
+ this.normals.push(nx, sinPhi, nz);
507
+ this.colors.push(color[0], color[1], color[2]);
508
+ this.emissive.push(emissive);
509
+ this.specular.push(specular);
510
+ this.pushEmissiveColor();
511
+ this.roughness.push(this.roughnessState ?? INHERIT_ROUGHNESS);
512
+ this.grain.push(this.grainState);
513
+ this.relief.push(this.reliefState);
514
+ }
515
+ }
516
+ this.vertexCount += rows * (segments + 1);
517
+ const stride = segments + 1;
518
+ for (let row = 0; row < rows - 1; row++) {
519
+ for (let segment = 0; segment < segments; segment++) {
520
+ const a = base + row * stride + segment;
521
+ const b = a + 1;
522
+ const c = a + stride;
523
+ const d = c + 1;
524
+ this.indices.push(a, c, b, b, c, d);
525
+ }
526
+ }
527
+ return this;
528
+ }
529
+ /**
530
+ * A smooth irregular solid: a sphere whose radius and colour are both asked for per point.
531
+ *
532
+ * **The gap this fills is "curved *and* varied", which nothing here could express.** Every
533
+ * other generator takes one colour for the whole call, and the one primitive that could vary
534
+ * it, `addQuad`, computes a *face* normal from its winding, so anything assembled from quads
535
+ * is flat by construction. A consumer wanting a planet with latitude bands therefore had a
536
+ * choice between a texture, in an engine whose whole argument is not needing images, and
537
+ * geometry stuck onto a sphere. And a consumer wanting a rock went through four attempts,
538
+ * each failing on a property of the primitive rather than on the shape: boxes gave right
539
+ * angles, overlapping spheres gave cusps at every intersection and read as foam, a quad grid
540
+ * gave a correct silhouette made of eighty flat plates, and a tube gave a water-worn pebble
541
+ * because its cross-section is always a circle.
542
+ *
543
+ * `radiusAt` and `color` both take the surface parameters, `u` around and `v` from pole to
544
+ * pole, each 0 to 1. A constant radius is `addSphere`; a few low-frequency waves is a boulder;
545
+ * a signed step is a fracture; a constant radius with a varying colour is a banded planet.
546
+ *
547
+ * **A `radiusAt` built as a `min` of constraints has to place them inside the base radius.** A
548
+ * cut plane at or above it removes nothing, so a set of them scattered around the radius
549
+ * leaves a shape that is nearly round more often than not. Reported from outside, where nine
550
+ * planes between 0.34 and 0.50 of a 0.5 radius gave a sphere about one seed in three.
551
+ *
552
+ * **The normal is derived from the surface rather than from the radius**, which is the part
553
+ * that matters and the part a radial normal gets wrong. Where the radius varies, the surface
554
+ * no longer faces along its own radius, so a radial normal lights a boulder as though it were
555
+ * a sphere: the silhouette is lumpy and the shading is not. Each vertex takes the cross
556
+ * product of the two parametric tangents, measured from the same callback, so a smooth radius
557
+ * gives smooth shading and an abrupt one gives a genuine hard edge. That is what lets one
558
+ * primitive be both a pebble and a fracture.
559
+ */
560
+ addBlob(center, radiusAt, color, emissive = 0, segments = 24, rings = 16, specular = 0) {
561
+ if (!Number.isInteger(segments) || segments < 3) {
562
+ throw new Error('Blob segments must be an integer of at least 3.');
563
+ }
564
+ if (!Number.isInteger(rings) || rings < 2) {
565
+ throw new Error('Blob rings must be an integer of at least 2.');
566
+ }
567
+ /* One place that turns parameters into a point, so the tangents below are measured against
568
+ exactly the surface the positions are on rather than against an idealised one. */
569
+ const pointAt = (u, v, out) => {
570
+ const theta = u * Math.PI * 2;
571
+ const phi = (v - 0.5) * Math.PI;
572
+ const cosPhi = Math.cos(phi);
573
+ const r = radiusAt(u, v);
574
+ if (!Number.isFinite(r) || r <= 0)
575
+ throw new Error('Blob radiusAt must return a positive number.');
576
+ out[0] = center[0] + cosPhi * Math.cos(theta) * r;
577
+ out[1] = center[1] + Math.sin(phi) * r;
578
+ out[2] = center[2] + cosPhi * Math.sin(theta) * r;
579
+ };
580
+ const base = this.vertexCount;
581
+ const here = [0, 0, 0];
582
+ const uPlus = [0, 0, 0];
583
+ const uMinus = [0, 0, 0];
584
+ const vPlus = [0, 0, 0];
585
+ const vMinus = [0, 0, 0];
586
+ /* A step small against one cell, so the difference measures the surface rather than the
587
+ grid, and large enough that a signed radius still reports its own jump. */
588
+ const hu = 1 / (segments * 8);
589
+ const hv = 1 / (rings * 8);
590
+ /*
591
+ * Everything this builder already holds gets a coordinate before the blob's own start, for
592
+ * the reason `mergeUvs` states: a short attribute buffer is read past its end as zeroes by
593
+ * some drivers and makes others drop the draw, and neither raises a GL error.
594
+ */
595
+ while (this.uvs.length < base * 2)
596
+ this.uvs.push(0);
597
+ for (let ring = 0; ring <= rings; ring++) {
598
+ const v = ring / rings;
599
+ for (let segment = 0; segment <= segments; segment++) {
600
+ const u = segment / segments;
601
+ pointAt(u, v, here);
602
+ /* Wrapped in u, because the seam is one surface and a one-sided difference there would
603
+ give the two edges of it different normals and draw a line down the object. */
604
+ pointAt((u + hu) % 1, v, uPlus);
605
+ pointAt((u - hu + 1) % 1, v, uMinus);
606
+ /* Clamped in v: the poles have no other side. */
607
+ pointAt(u, Math.min(1, v + hv), vPlus);
608
+ pointAt(u, Math.max(0, v - hv), vMinus);
609
+ const tu = [
610
+ uPlus[0] - uMinus[0],
611
+ uPlus[1] - uMinus[1],
612
+ uPlus[2] - uMinus[2],
613
+ ];
614
+ const tv = [
615
+ vPlus[0] - vMinus[0],
616
+ vPlus[1] - vMinus[1],
617
+ vPlus[2] - vMinus[2],
618
+ ];
619
+ let nx = tv[1] * tu[2] - tv[2] * tu[1];
620
+ let ny = tv[2] * tu[0] - tv[0] * tu[2];
621
+ let nz = tv[0] * tu[1] - tv[1] * tu[0];
622
+ const length = Math.hypot(nx, ny, nz);
623
+ if (length > 1e-9) {
624
+ nx /= length;
625
+ ny /= length;
626
+ nz /= length;
627
+ }
628
+ else {
629
+ /* Degenerate, which is the pole: the tangent around vanishes there. The radial
630
+ direction is the honest answer and is what a sphere would have given anyway. */
631
+ const rx = here[0] - center[0];
632
+ const ry = here[1] - center[1];
633
+ const rz = here[2] - center[2];
634
+ const radial = Math.hypot(rx, ry, rz) || 1;
635
+ nx = rx / radial;
636
+ ny = ry / radial;
637
+ nz = rz / radial;
638
+ }
639
+ const tint = typeof color === 'function' ? color(u, v) : color;
640
+ /*
641
+ * The surface parameters, kept rather than dropped, which is what lets a round thing
642
+ * wear an authored image at all.
643
+ *
644
+ * They cost nothing to produce: the loop is already walking them to place the vertex,
645
+ * and `planarUvs` is no substitute, being a box projection that meets itself at three
646
+ * seams and smears the poles along an axis. What they cost is the buffer — two floats a
647
+ * vertex, so a 240 by 130 sphere carries about 250 KB of them whether it is textured or
648
+ * not. That is the same bargain `grain` and `relief` make, and it is why a mesh with no
649
+ * blob in it still emits nothing.
650
+ *
651
+ * `u` runs 0 to 1 around and `v` 0 to 1 pole to pole, which is equirectangular and is
652
+ * the parameterisation the primitive already had. The last column repeats the first at
653
+ * `u = 1`, which exists here for the normals and is exactly what a wrapped image wants.
654
+ */
655
+ this.uvs.push(u, v);
656
+ this.positions.push(here[0], here[1], here[2]);
657
+ this.normals.push(nx, ny, nz);
658
+ this.colors.push(tint[0], tint[1], tint[2]);
659
+ this.emissive.push(emissive);
660
+ this.specular.push(specular);
661
+ this.pushEmissiveColor();
662
+ this.roughness.push(this.roughnessState ?? INHERIT_ROUGHNESS);
663
+ this.grain.push(this.grainState);
664
+ this.relief.push(this.reliefState);
665
+ }
666
+ }
667
+ this.vertexCount += (rings + 1) * (segments + 1);
668
+ const stride = segments + 1;
669
+ for (let ring = 0; ring < rings; ring++) {
670
+ for (let segment = 0; segment < segments; segment++) {
671
+ const a = base + ring * stride + segment;
672
+ const b = a + 1;
673
+ const c = a + stride;
674
+ const d = c + 1;
675
+ this.indices.push(a, c, b, b, c, d);
676
+ }
677
+ }
678
+ return this;
679
+ }
680
+ /** A sphere, which is a capsule with no shaft. */
681
+ addSphere(center, radius, color, emissive = 0, segments = 12, rings = 6) {
682
+ return this.addCapsule(center, radius, 0, color, emissive, segments, rings);
683
+ }
684
+ /** Add a faceted cylinder aligned to one principal axis. */
685
+ addCylinder(center, radius, halfLength, axis, color, emissive = 0, segments = 12, specular = 0) {
686
+ if (radius <= 0 || halfLength <= 0) {
687
+ throw new Error('Cylinder radius and half-length must be positive.');
688
+ }
689
+ if (!Number.isInteger(segments) || segments < 3) {
690
+ throw new Error('Cylinder segments must be an integer of at least 3.');
691
+ }
692
+ const a = axis === 'x' ? 0 : axis === 'y' ? 1 : 2;
693
+ const b = (a + 1) % 3;
694
+ const c = (a + 2) % 3;
695
+ const positiveCapNormal = [0, 0, 0];
696
+ const negativeCapNormal = [0, 0, 0];
697
+ const positiveCenter = [center[0], center[1], center[2]];
698
+ const negativeCenter = [center[0], center[1], center[2]];
699
+ positiveCapNormal[a] = 1;
700
+ negativeCapNormal[a] = -1;
701
+ positiveCenter[a] += halfLength;
702
+ negativeCenter[a] -= halfLength;
703
+ for (let segment = 0; segment < segments; segment++) {
704
+ const angle0 = (segment / segments) * Math.PI * 2;
705
+ const angle1 = ((segment + 1) / segments) * Math.PI * 2;
706
+ const cos0 = Math.cos(angle0);
707
+ const sin0 = Math.sin(angle0);
708
+ const cos1 = Math.cos(angle1);
709
+ const sin1 = Math.sin(angle1);
710
+ const sideNormal = [0, 0, 0];
711
+ const midAngle = (angle0 + angle1) * 0.5;
712
+ sideNormal[b] = Math.cos(midAngle);
713
+ sideNormal[c] = Math.sin(midAngle);
714
+ const negative0 = [center[0], center[1], center[2]];
715
+ const negative1 = [center[0], center[1], center[2]];
716
+ const positive0 = [center[0], center[1], center[2]];
717
+ const positive1 = [center[0], center[1], center[2]];
718
+ negative0[a] -= halfLength;
719
+ negative1[a] -= halfLength;
720
+ positive0[a] += halfLength;
721
+ positive1[a] += halfLength;
722
+ negative0[b] += cos0 * radius;
723
+ negative0[c] += sin0 * radius;
724
+ negative1[b] += cos1 * radius;
725
+ negative1[c] += sin1 * radius;
726
+ positive0[b] += cos0 * radius;
727
+ positive0[c] += sin0 * radius;
728
+ positive1[b] += cos1 * radius;
729
+ positive1[c] += sin1 * radius;
730
+ let i = this.vertexCount;
731
+ this.addVertex(negative0, sideNormal, color, emissive, specular);
732
+ this.addVertex(negative1, sideNormal, color, emissive, specular);
733
+ this.addVertex(positive1, sideNormal, color, emissive, specular);
734
+ this.addVertex(positive0, sideNormal, color, emissive, specular);
735
+ this.indices.push(i, i + 1, i + 2, i, i + 2, i + 3);
736
+ i = this.vertexCount;
737
+ this.addVertex(positiveCenter, positiveCapNormal, color, emissive, specular);
738
+ this.addVertex(positive0, positiveCapNormal, color, emissive, specular);
739
+ this.addVertex(positive1, positiveCapNormal, color, emissive, specular);
740
+ this.indices.push(i, i + 1, i + 2);
741
+ i = this.vertexCount;
742
+ this.addVertex(negativeCenter, negativeCapNormal, color, emissive, specular);
743
+ this.addVertex(negative1, negativeCapNormal, color, emissive, specular);
744
+ this.addVertex(negative0, negativeCapNormal, color, emissive, specular);
745
+ this.indices.push(i, i + 1, i + 2);
746
+ }
747
+ return this;
748
+ }
749
+ /**
750
+ * Sweep a round tube of varying radius along an arbitrary path.
751
+ *
752
+ * The primitive a *curve* needs. `addCylinder` is axis-aligned and straight, and a
753
+ * curve chained out of boxes — which is how every beam in this engine's consumers
754
+ * has been built — has hard corners and only six distinct normals, so it reads as
755
+ * a staircase however finely it is stepped. A tube carries a normal per ring
756
+ * vertex, so the flat shader's interpolated `vNormal` shades it as the round thing
757
+ * it is.
758
+ *
759
+ * The frame is **parallel-transported** rather than built from a fixed world up.
760
+ * A frame derived from world up flips through 180 degrees wherever the path turns
761
+ * vertical, which puts a visible twist in the tube at exactly the apex of an arch;
762
+ * carrying the previous ring's frame forward and re-orthogonalising it against the
763
+ * new tangent has no such singularity. It also has no preferred orientation to
764
+ * disagree with, which matters when the path is planar and the plane is arbitrary.
765
+ *
766
+ * @param path Centreline as flattened x, y, z triples. At least two points.
767
+ * @param radii One radius per path point, so a sweep can taper.
768
+ * @param sides Vertices per ring. Eight is round enough to read as a cylinder at
769
+ * the distance a fixture is seen from, and cheap enough to put one on every gate.
770
+ */
771
+ /* Open at both ends: this draws a wall and no caps. Taper the end radii to almost nothing, or
772
+ cap it yourself. A tube used as a solid shows a hole at one bearing per orbit, which survives
773
+ a screenshot and fails in motion. */
774
+ addTube(path, radii, color, emissive = 0, sides = 8) {
775
+ const count = Math.floor(path.length / 3);
776
+ if (count < 2)
777
+ throw new Error(`A tube needs at least two path points, got ${count}.`);
778
+ if (radii.length !== count) {
779
+ throw new Error(`A tube needs one radius per path point: ${radii.length} for ${count}.`);
780
+ }
781
+ if (!Number.isInteger(sides) || sides < 3) {
782
+ throw new Error(`Tube sides must be an integer of at least 3, got ${sides}.`);
783
+ }
784
+ const px = (i) => path[i * 3] ?? 0;
785
+ const py = (i) => path[i * 3 + 1] ?? 0;
786
+ const pz = (i) => path[i * 3 + 2] ?? 0;
787
+ const base = this.vertexCount;
788
+ // Carried between rings: the transported reference axis.
789
+ let ux = 0;
790
+ let uy = 0;
791
+ let uz = 0;
792
+ let seeded = false;
793
+ for (let i = 0; i < count; i++) {
794
+ // Central difference inside, one-sided at the ends.
795
+ const a = Math.max(i - 1, 0);
796
+ const b = Math.min(i + 1, count - 1);
797
+ let tx = px(b) - px(a);
798
+ let ty = py(b) - py(a);
799
+ let tz = pz(b) - pz(a);
800
+ const tLength = Math.hypot(tx, ty, tz) || 1;
801
+ tx /= tLength;
802
+ ty /= tLength;
803
+ tz /= tLength;
804
+ if (!seeded) {
805
+ // Any axis not parallel to the tangent will do for the first ring; every
806
+ // later one is transported from it, so the choice never shows.
807
+ const ax = Math.abs(tx) < 0.9 ? 1 : 0;
808
+ const ay = Math.abs(tx) < 0.9 ? 0 : 1;
809
+ ux = ay * tz - 0 * ty;
810
+ uy = 0 * tx - ax * tz;
811
+ uz = ax * ty - ay * tx;
812
+ seeded = true;
813
+ }
814
+ // Re-orthogonalise against this ring's tangent: the minimal rotation, which is
815
+ // what keeps the sweep from twisting along its own length.
816
+ const drift = ux * tx + uy * ty + uz * tz;
817
+ ux -= tx * drift;
818
+ uy -= ty * drift;
819
+ uz -= tz * drift;
820
+ const uLength = Math.hypot(ux, uy, uz);
821
+ if (uLength < 1e-6) {
822
+ // The path doubled back on itself. Reseed rather than emit NaN.
823
+ ux = Math.abs(tx) < 0.9 ? 1 - tx * tx : -tx * ty;
824
+ uy = Math.abs(tx) < 0.9 ? -tx * ty : 1 - ty * ty;
825
+ uz = Math.abs(tx) < 0.9 ? -tx * tz : -ty * tz;
826
+ const reseeded = Math.hypot(ux, uy, uz) || 1;
827
+ ux /= reseeded;
828
+ uy /= reseeded;
829
+ uz /= reseeded;
830
+ }
831
+ else {
832
+ ux /= uLength;
833
+ uy /= uLength;
834
+ uz /= uLength;
835
+ }
836
+ // v completes the right-handed ring basis: u, v, tangent.
837
+ const vx = ty * uz - tz * uy;
838
+ const vy = tz * ux - tx * uz;
839
+ const vz = tx * uy - ty * ux;
840
+ const radius = radii[i] ?? 0;
841
+ for (let j = 0; j < sides; j++) {
842
+ const angle = (j / sides) * Math.PI * 2;
843
+ const cos = Math.cos(angle);
844
+ const sin = Math.sin(angle);
845
+ const nx = ux * cos + vx * sin;
846
+ const ny = uy * cos + vy * sin;
847
+ const nz = uz * cos + vz * sin;
848
+ this.addVertex([px(i) + nx * radius, py(i) + ny * radius, pz(i) + nz * radius], [nx, ny, nz], color, emissive);
849
+ }
850
+ }
851
+ /*
852
+ * Stitched (i,j) → (i,j+1) → (i+1,j+1) → (i+1,j), which is the winding that comes
853
+ * out facing *outward*: the other order yields `tangent x (tangent x u) = -u`,
854
+ * an inward normal, and the whole tube is then culled away.
855
+ */
856
+ for (let i = 0; i + 1 < count; i++) {
857
+ for (let j = 0; j < sides; j++) {
858
+ const next = (j + 1) % sides;
859
+ const near = base + i * sides;
860
+ const far = base + (i + 1) * sides;
861
+ this.indices.push(near + j, near + next, far + next);
862
+ this.indices.push(near + j, far + next, far + j);
863
+ }
864
+ }
865
+ return this;
866
+ }
867
+ /**
868
+ * How much geometry is in here so far, without building it.
869
+ *
870
+ * **So a caller batching by size can ask instead of counting.** A consumer streaming a world caps
871
+ * a batch at a triangle count and opens another when it is passed; without this the only way to
872
+ * know was to tally what it had handed over, which means every geometry verb's triangle count
873
+ * duplicated on the caller's side, and a batch that overshoots by whatever the last object was —
874
+ * measured at 47,040 triangles against a cap of 32,000.
875
+ *
876
+ * Both are counters, not scans: asking costs nothing and may be asked between every object.
877
+ */
878
+ get triangleCount() {
879
+ return this.indices.length / 3;
880
+ }
881
+ /** Vertices written so far. The other half of the same question, for a memory budget. */
882
+ get vertexCountSoFar() {
883
+ return this.vertexCount;
884
+ }
885
+ build(options = {}) {
886
+ /*
887
+ * The specular array is emitted only when something in it is non-zero.
888
+ *
889
+ * That keeps the promise `MeshData.specular` makes: geometry that does not shine
890
+ * produces exactly the mesh it produced before this attribute existed, uploads no
891
+ * extra buffer, and reads the constant a disabled vertex attribute supplies.
892
+ */
893
+ /*
894
+ * **Asked of the list rather than scanned for.** Each of these is "did anything differ from
895
+ * the default", and each used to be a `some()` over every vertex — about 1.5 million predicate
896
+ * calls for a mesh of a hundred thousand triangles, on the one call whose whole job is to be
897
+ * cheap. The list knows at write time and answers in a field.
898
+ */
899
+ const shines = this.specular.hasOther;
900
+ /* Same promise as `specular`: absent unless something actually named a colour. */
901
+ const tinted = this.emissiveColor.hasOther;
902
+ const rough = this.roughness.hasOther;
903
+ /* Same promise again: a world with no mineral surfaces uploads no grain buffer. */
904
+ const mineral = this.grain.hasOther;
905
+ /* And again: a world with no textured surfaces uploads no relief buffer. */
906
+ const textured = this.relief.hasOther;
907
+ /* Same promise as `specular`: a mesh nothing bound uploads no skinning buffers, and
908
+ `MeshData` requires both arrays or neither. */
909
+ const skinned = this.anyJoint;
910
+ return {
911
+ positions: this.positions.toTyped(),
912
+ normals: this.normals.toTyped(),
913
+ colors: this.colors.toTyped(),
914
+ emissive: this.emissive.toTyped(),
915
+ ...(shines ? { specular: this.specular.toTyped() } : {}),
916
+ ...(textured ? { relief: this.relief.toTyped() } : {}),
917
+ ...(tinted ? { emissiveColor: this.emissiveColor.toTyped() } : {}),
918
+ ...(rough ? { roughness: this.roughness.toTyped() } : {}),
919
+ ...(mineral ? { grain: this.grain.toTyped() } : {}),
920
+ ...(skinned ? { joints: this.expandedJoints(), weights: this.expandedWeights() } : {}),
921
+ ...(options.planarUvs === true
922
+ ? { uvs: this.planarUvs() }
923
+ : this.uvs.length > 0
924
+ ? { uvs: this.paddedUvs() }
925
+ : {}),
926
+ indices: this.indices.toTyped(),
927
+ };
928
+ }
929
+ /**
930
+ * Take a merged mesh's own texture coordinates, if it brought any.
931
+ *
932
+ * Padded to this mesh's own start first, because everything built before it — a box, a
933
+ * cylinder, a mesh carrying no coordinates — contributed vertices and no UVs. Without the
934
+ * padding the array is short, and every coordinate after the first gap belongs to the
935
+ * wrong vertex, which draws as a texture sliding across a surface rather than as an error.
936
+ *
937
+ * It is the same defect `roughness` had here, and the reason it is worth stating twice: a
938
+ * short attribute buffer is read past the end as zeroes by some drivers and causes others
939
+ * to drop the draw outright, and neither raises a GL error.
940
+ */
941
+ mergeUvs(mesh, base, count) {
942
+ if (mesh.uvs === undefined)
943
+ return;
944
+ while (this.uvs.length < base * 2)
945
+ this.uvs.push(0);
946
+ for (let i = 0; i < count; i++) {
947
+ this.uvs.push(mesh.uvs[i * 2] ?? 0, mesh.uvs[i * 2 + 1] ?? 0);
948
+ }
949
+ }
950
+ /**
951
+ * The rigid bindings, run out to cover every vertex the builder holds.
952
+ *
953
+ * A vertex before the first `setJoint` answers joint 0 at full weight rather than zero
954
+ * weight, and the distinction is not pedantic: the shader sums four matrices by four
955
+ * weights, so a zero-weight vertex is multiplied to the origin and the geometry reads as
956
+ * exploding toward the rig's root. Following the root is what a rigid mesh under a skeleton
957
+ * does, and it is the answer that degrades to something sensible.
958
+ */
959
+ expandedJoints() {
960
+ const total = this.positions.length / 3;
961
+ const out = new Float32Array(total * 4);
962
+ for (let v = 0, span = 0, joint = 0; v < total; v++) {
963
+ while (span < this.jointSpans.length && (this.jointSpans[span]?.from ?? 0) <= v) {
964
+ joint = this.jointSpans[span]?.joint ?? 0;
965
+ span++;
966
+ }
967
+ out[v * 4] = joint;
968
+ }
969
+ return out;
970
+ }
971
+ /** One influence at full weight, per vertex. See `expandedJoints`. */
972
+ expandedWeights() {
973
+ const total = this.positions.length / 3;
974
+ const out = new Float32Array(total * 4);
975
+ for (let v = 0; v < total; v++)
976
+ out[v * 4] = 1;
977
+ return out;
978
+ }
979
+ /** The merged coordinates, run out to cover every vertex the builder holds. */
980
+ paddedUvs() {
981
+ const out = new Float32Array(this.vertexCount * 2);
982
+ const merged = this.uvs;
983
+ for (let i = 0; i < out.length && i < merged.length; i++)
984
+ out[i] = merged.at(i);
985
+ return out;
986
+ }
987
+ /**
988
+ * Box-project every vertex onto the plane of the face it belongs to, in metres.
989
+ *
990
+ * Derived here rather than accumulated per vertex, and that is what keeps the cost
991
+ * genuinely opt-in: UVs are a pure function of position and normal, so nothing
992
+ * upstream — not `addBox`, not the cylinder, not a merged mesh — needs a second push
993
+ * site or a branch, and a caller that never asks for UVs allocates nothing anywhere.
994
+ *
995
+ * The projection uses **world position** rather than a per-face 0–1 range, so a
996
+ * surface assembled from many separate boxes tiles continuously across the joins
997
+ * instead of restarting the pattern at each one. That is what a corridor built out of
998
+ * wall segments actually needs, and it is why texture density is a repeats-per-metre
999
+ * number at draw time rather than something baked in here.
1000
+ *
1001
+ * Axis choice mirrors `addFace`: for a face whose normal is dominant on axis `a`, U
1002
+ * runs along `(a+1)%3` and V along `(a+2)%3`. The two agree by construction rather
1003
+ * than by coincidence, which is what makes a box's UVs continuous with its
1004
+ * neighbours'.
1005
+ */
1006
+ planarUvs() {
1007
+ const uvs = new Float32Array(this.vertexCount * 2);
1008
+ for (let i = 0; i < this.vertexCount; i++) {
1009
+ const nx = Math.abs(this.normals.at(i * 3));
1010
+ const ny = Math.abs(this.normals.at(i * 3 + 1));
1011
+ const nz = Math.abs(this.normals.at(i * 3 + 2));
1012
+ const axis = nx >= ny && nx >= nz ? 0 : ny >= nz ? 1 : 2;
1013
+ const x = this.positions.at(i * 3);
1014
+ const y = this.positions.at(i * 3 + 1);
1015
+ const z = this.positions.at(i * 3 + 2);
1016
+ /*
1017
+ * V is world *up* on anything vertical, and the two ground axes on anything flat.
1018
+ *
1019
+ * Rotating the axis index to pick the pair is the tidy-looking version and it is
1020
+ * wrong: it gives a wall facing X the pair (Y, Z) and a wall facing Z the pair
1021
+ * (X, Y), so U runs upward on one and along the floor on the other. Every texture
1022
+ * with an orientation — which is every wallpaper, every tile with a grain — then
1023
+ * appears rotated a quarter turn depending on which way the wall happens to face,
1024
+ * on adjacent walls of the same room. Reported exactly that way: one side of the
1025
+ * corridor right, the other "flipped by 90 degrees".
1026
+ */
1027
+ if (axis === 0) {
1028
+ uvs[i * 2] = z;
1029
+ uvs[i * 2 + 1] = y;
1030
+ }
1031
+ else if (axis === 1) {
1032
+ uvs[i * 2] = x;
1033
+ uvs[i * 2 + 1] = z;
1034
+ }
1035
+ else {
1036
+ uvs[i * 2] = x;
1037
+ uvs[i * 2 + 1] = y;
1038
+ }
1039
+ }
1040
+ return uvs;
1041
+ }
1042
+ addFace(center, half, color, emissive, axis, sign, specular = 0) {
1043
+ const b = (axis + 1) % 3;
1044
+ const c = (axis + 2) % 3;
1045
+ // Face center offset along the normal; U along axis b, V along sign·axis c.
1046
+ const normal = [0, 0, 0];
1047
+ normal[axis] = sign;
1048
+ const base = [center[0], center[1], center[2]];
1049
+ base[axis] += sign * half[axis];
1050
+ for (const [du, dv] of CORNERS) {
1051
+ const point = [base[0], base[1], base[2]];
1052
+ point[b] += du * half[b];
1053
+ point[c] += sign * dv * half[c];
1054
+ this.positions.push(point[0], point[1], point[2]);
1055
+ this.normals.push(normal[0], normal[1], normal[2]);
1056
+ this.colors.push(color[0], color[1], color[2]);
1057
+ this.emissive.push(emissive);
1058
+ this.specular.push(specular);
1059
+ this.pushEmissiveColor();
1060
+ this.roughness.push(this.roughnessState ?? INHERIT_ROUGHNESS);
1061
+ this.grain.push(this.grainState);
1062
+ this.relief.push(this.reliefState);
1063
+ }
1064
+ const i = this.vertexCount;
1065
+ this.indices.push(i, i + 1, i + 2, i, i + 2, i + 3);
1066
+ this.vertexCount += 4;
1067
+ }
1068
+ pushEmissiveColor() {
1069
+ const color = this.emissiveColorState;
1070
+ if (color === null) {
1071
+ this.emissiveColor.push(INHERIT_ALBEDO, INHERIT_ALBEDO, INHERIT_ALBEDO);
1072
+ return;
1073
+ }
1074
+ this.emissiveColor.push(color[0], color[1], color[2]);
1075
+ }
1076
+ addVertex(position, normal, color, emissive, specular = 0) {
1077
+ this.positions.push(position[0], position[1], position[2]);
1078
+ this.normals.push(normal[0], normal[1], normal[2]);
1079
+ this.colors.push(color[0], color[1], color[2]);
1080
+ this.emissive.push(emissive);
1081
+ this.specular.push(specular);
1082
+ this.pushEmissiveColor();
1083
+ this.roughness.push(this.roughnessState ?? INHERIT_ROUGHNESS);
1084
+ this.grain.push(this.grainState);
1085
+ this.relief.push(this.reliefState);
1086
+ this.vertexCount++;
1087
+ }
1088
+ }
1089
+ /** Tolerance on each axis's length and on each pair's dot product. */
1090
+ const BASIS_EPSILON = 1e-3;
1091
+ function assertOrthonormal(right, up, forward) {
1092
+ const lengths = Math.abs(Math.hypot(right[0], right[1], right[2]) - 1) +
1093
+ Math.abs(Math.hypot(up[0], up[1], up[2]) - 1) +
1094
+ Math.abs(Math.hypot(forward[0], forward[1], forward[2]) - 1);
1095
+ const skew = Math.abs(dot(right, up)) + Math.abs(dot(up, forward)) + Math.abs(dot(forward, right));
1096
+ if (lengths > BASIS_EPSILON * 3 || skew > BASIS_EPSILON * 3) {
1097
+ throw new Error(`addOrientedMesh needs an orthonormal basis (length error ${lengths.toFixed(4)}, skew ${skew.toFixed(4)})`);
1098
+ }
1099
+ /*
1100
+ * And a right-handed one. A mirror reverses winding, so every face of a
1101
+ * left-handed placement comes out back-facing and is culled: geometry that
1102
+ * renders with no GL error and no pixels, which is among the hardest things
1103
+ * there is to look at and diagnose. The text renderer's own cube carries a
1104
+ * comment about the same trap from the other side.
1105
+ */
1106
+ const handed = right[0] * (up[1] * forward[2] - up[2] * forward[1]) +
1107
+ right[1] * (up[2] * forward[0] - up[0] * forward[2]) +
1108
+ right[2] * (up[0] * forward[1] - up[1] * forward[0]);
1109
+ if (handed <= 0) {
1110
+ throw new Error(`addOrientedMesh needs a right-handed basis; this one mirrors (determinant ${handed.toFixed(4)}) and every face would be culled`);
1111
+ }
1112
+ }
1113
+ function dot(a, b) {
1114
+ return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
1115
+ }
1116
+ const SIGNS = [1, -1];
1117
+ /**
1118
+ * The unnormalised face normal of a quad, by `addQuad`'s own rule so the wrappers cannot disagree
1119
+ * with the method they delegate to.
1120
+ *
1121
+ * Unnormalised because both callers only read its *sign* against a direction, and reused at module
1122
+ * scope because building a world calls this once per quad.
1123
+ */
1124
+ const QUAD_NORMAL = [0, 0, 0];
1125
+ function quadNormal(a, b, d) {
1126
+ const ux = b[0] - a[0];
1127
+ const uy = b[1] - a[1];
1128
+ const uz = b[2] - a[2];
1129
+ const vx = d[0] - a[0];
1130
+ const vy = d[1] - a[1];
1131
+ const vz = d[2] - a[2];
1132
+ QUAD_NORMAL[0] = uy * vz - uz * vy;
1133
+ QUAD_NORMAL[1] = uz * vx - ux * vz;
1134
+ QUAD_NORMAL[2] = ux * vy - uy * vx;
1135
+ if (QUAD_NORMAL[0] === 0 && QUAD_NORMAL[1] === 0 && QUAD_NORMAL[2] === 0) {
1136
+ throw new Error('degenerate corners have no normal');
1137
+ }
1138
+ return QUAD_NORMAL;
1139
+ }
1140
+ const BOX_FACES = [
1141
+ [
1142
+ [-1, -1, 1],
1143
+ [1, -1, 1],
1144
+ [1, 1, 1],
1145
+ [-1, 1, 1],
1146
+ ],
1147
+ [
1148
+ [-1, -1, -1],
1149
+ [1, -1, -1],
1150
+ [1, 1, -1],
1151
+ [-1, 1, -1],
1152
+ ],
1153
+ [
1154
+ [1, -1, -1],
1155
+ [1, -1, 1],
1156
+ [1, 1, 1],
1157
+ [1, 1, -1],
1158
+ ],
1159
+ [
1160
+ [-1, -1, -1],
1161
+ [-1, -1, 1],
1162
+ [-1, 1, 1],
1163
+ [-1, 1, -1],
1164
+ ],
1165
+ [
1166
+ [-1, 1, -1],
1167
+ [1, 1, -1],
1168
+ [1, 1, 1],
1169
+ [-1, 1, 1],
1170
+ ],
1171
+ [
1172
+ [-1, -1, -1],
1173
+ [1, -1, -1],
1174
+ [1, -1, 1],
1175
+ [-1, -1, 1],
1176
+ ],
1177
+ ];
1178
+ /** Corner order (-U-V, +U-V, +U+V, -U+V) → two CCW triangles per face. */
1179
+ const CORNERS = [
1180
+ [-1, -1],
1181
+ [1, -1],
1182
+ [1, 1],
1183
+ [-1, 1],
1184
+ ];