@thunderclap-engine/engine 0.0.0-stage → 1.1.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 (566) hide show
  1. package/CHANGELOG.md +390 -0
  2. package/LICENSE +21 -0
  3. package/README.md +333 -2
  4. package/dist/analytics/analytics.d.ts +80 -0
  5. package/dist/analytics/analytics.js +129 -0
  6. package/dist/analytics/index.d.ts +1 -0
  7. package/dist/analytics/index.js +1 -0
  8. package/dist/anim/aim.d.ts +43 -0
  9. package/dist/anim/aim.js +157 -0
  10. package/dist/anim/clip.d.ts +98 -0
  11. package/dist/anim/clip.js +282 -0
  12. package/dist/anim/compress.d.ts +65 -0
  13. package/dist/anim/compress.js +339 -0
  14. package/dist/anim/foot-ik.d.ts +74 -0
  15. package/dist/anim/foot-ik.js +241 -0
  16. package/dist/anim/graph.d.ts +153 -0
  17. package/dist/anim/graph.js +532 -0
  18. package/dist/anim/ik.d.ts +105 -0
  19. package/dist/anim/ik.js +435 -0
  20. package/dist/anim/index.d.ts +36 -0
  21. package/dist/anim/index.js +21 -0
  22. package/dist/anim/match.d.ts +66 -0
  23. package/dist/anim/match.js +153 -0
  24. package/dist/anim/mixer.d.ts +113 -0
  25. package/dist/anim/mixer.js +314 -0
  26. package/dist/anim/morph.d.ts +41 -0
  27. package/dist/anim/morph.js +94 -0
  28. package/dist/anim/pose.d.ts +74 -0
  29. package/dist/anim/pose.js +254 -0
  30. package/dist/anim/quat.d.ts +44 -0
  31. package/dist/anim/quat.js +187 -0
  32. package/dist/anim/retarget.d.ts +97 -0
  33. package/dist/anim/retarget.js +366 -0
  34. package/dist/anim/root-motion.d.ts +44 -0
  35. package/dist/anim/root-motion.js +224 -0
  36. package/dist/anim/skin-pose.d.ts +39 -0
  37. package/dist/anim/skin-pose.js +53 -0
  38. package/dist/anim/socket.d.ts +47 -0
  39. package/dist/anim/socket.js +84 -0
  40. package/dist/anim/spring-chain.d.ts +73 -0
  41. package/dist/anim/spring-chain.js +294 -0
  42. package/dist/assets/atlas.d.ts +90 -0
  43. package/dist/assets/atlas.js +212 -0
  44. package/dist/assets/baked.d.ts +33 -0
  45. package/dist/assets/baked.js +38 -0
  46. package/dist/assets/bundle.d.ts +88 -0
  47. package/dist/assets/bundle.js +464 -0
  48. package/dist/assets/cache.d.ts +56 -0
  49. package/dist/assets/cache.js +123 -0
  50. package/dist/assets/gltf-load.d.ts +252 -0
  51. package/dist/assets/gltf-load.js +661 -0
  52. package/dist/assets/gltf-skin.d.ts +36 -0
  53. package/dist/assets/gltf-skin.js +109 -0
  54. package/dist/assets/gltf.d.ts +706 -0
  55. package/dist/assets/gltf.js +1376 -0
  56. package/dist/assets/index.d.ts +20 -0
  57. package/dist/assets/index.js +10 -0
  58. package/dist/assets/ktx2.d.ts +71 -0
  59. package/dist/assets/ktx2.js +213 -0
  60. package/dist/assets/manifest.d.ts +79 -0
  61. package/dist/assets/manifest.js +7 -0
  62. package/dist/assets/meshopt.d.ts +23 -0
  63. package/dist/assets/meshopt.js +403 -0
  64. package/dist/audio/audio.d.ts +733 -0
  65. package/dist/audio/audio.js +2013 -0
  66. package/dist/audio/dsp.d.ts +61 -0
  67. package/dist/audio/dsp.js +159 -0
  68. package/dist/audio/duck.d.ts +44 -0
  69. package/dist/audio/duck.js +101 -0
  70. package/dist/audio/envelope.d.ts +53 -0
  71. package/dist/audio/envelope.js +106 -0
  72. package/dist/audio/index.d.ts +13 -0
  73. package/dist/audio/index.js +14 -0
  74. package/dist/audio/music.d.ts +113 -0
  75. package/dist/audio/music.js +548 -0
  76. package/dist/audio/occlusion.d.ts +55 -0
  77. package/dist/audio/occlusion.js +61 -0
  78. package/dist/audio/persist.d.ts +23 -0
  79. package/dist/audio/persist.js +27 -0
  80. package/dist/audio/recipes.d.ts +55 -0
  81. package/dist/audio/recipes.js +227 -0
  82. package/dist/audio/send.d.ts +41 -0
  83. package/dist/audio/send.js +118 -0
  84. package/dist/audio/sprites.d.ts +59 -0
  85. package/dist/audio/sprites.js +43 -0
  86. package/dist/audio/synth.d.ts +103 -0
  87. package/dist/audio/synth.js +197 -0
  88. package/dist/audio/variations.d.ts +25 -0
  89. package/dist/audio/variations.js +55 -0
  90. package/dist/audio/zones.d.ts +100 -0
  91. package/dist/audio/zones.js +167 -0
  92. package/dist/core/detached-surface.d.ts +44 -0
  93. package/dist/core/detached-surface.js +104 -0
  94. package/dist/core/events.d.ts +24 -0
  95. package/dist/core/events.js +43 -0
  96. package/dist/core/game.d.ts +409 -0
  97. package/dist/core/game.js +530 -0
  98. package/dist/core/hash.d.ts +24 -0
  99. package/dist/core/hash.js +49 -0
  100. package/dist/core/index.d.ts +17 -0
  101. package/dist/core/index.js +9 -0
  102. package/dist/core/loop.d.ts +113 -0
  103. package/dist/core/loop.js +185 -0
  104. package/dist/core/math.d.ts +58 -0
  105. package/dist/core/math.js +210 -0
  106. package/dist/core/noise.d.ts +40 -0
  107. package/dist/core/noise.js +247 -0
  108. package/dist/core/pool.d.ts +42 -0
  109. package/dist/core/pool.js +85 -0
  110. package/dist/core/random.d.ts +61 -0
  111. package/dist/core/random.js +199 -0
  112. package/dist/core/scene.d.ts +79 -0
  113. package/dist/core/scene.js +195 -0
  114. package/dist/core/timer.d.ts +66 -0
  115. package/dist/core/timer.js +286 -0
  116. package/dist/fx/billboard-wgsl.d.ts +29 -0
  117. package/dist/fx/billboard-wgsl.js +158 -0
  118. package/dist/fx/billboard.d.ts +249 -0
  119. package/dist/fx/billboard.js +702 -0
  120. package/dist/fx/debug-draw.d.ts +127 -0
  121. package/dist/fx/debug-draw.js +523 -0
  122. package/dist/fx/debug-wgsl.d.ts +7 -0
  123. package/dist/fx/debug-wgsl.js +32 -0
  124. package/dist/fx/decals-wgsl.d.ts +15 -0
  125. package/dist/fx/decals-wgsl.js +173 -0
  126. package/dist/fx/decals.d.ts +216 -0
  127. package/dist/fx/decals.js +607 -0
  128. package/dist/fx/effect.d.ts +129 -0
  129. package/dist/fx/effect.js +349 -0
  130. package/dist/fx/effects/hit-spark.json +48 -0
  131. package/dist/fx/effects/index.d.ts +8 -0
  132. package/dist/fx/effects/index.js +9 -0
  133. package/dist/fx/effects/magic-aura.json +47 -0
  134. package/dist/fx/effects/smoke-puff.json +34 -0
  135. package/dist/fx/emit-surface.d.ts +40 -0
  136. package/dist/fx/emit-surface.js +124 -0
  137. package/dist/fx/flicker.d.ts +30 -0
  138. package/dist/fx/flicker.js +42 -0
  139. package/dist/fx/index.d.ts +31 -0
  140. package/dist/fx/index.js +20 -0
  141. package/dist/fx/particles-wgsl.d.ts +57 -0
  142. package/dist/fx/particles-wgsl.js +828 -0
  143. package/dist/fx/particles.d.ts +566 -0
  144. package/dist/fx/particles.js +1533 -0
  145. package/dist/fx/random.d.ts +15 -0
  146. package/dist/fx/random.js +20 -0
  147. package/dist/fx/sky-wgsl.d.ts +23 -0
  148. package/dist/fx/sky-wgsl.js +127 -0
  149. package/dist/fx/sky.d.ts +96 -0
  150. package/dist/fx/sky.js +175 -0
  151. package/dist/fx/text-atlas.d.ts +143 -0
  152. package/dist/fx/text-atlas.js +327 -0
  153. package/dist/fx/trail-wgsl.d.ts +11 -0
  154. package/dist/fx/trail-wgsl.js +42 -0
  155. package/dist/fx/trail.d.ts +194 -0
  156. package/dist/fx/trail.js +555 -0
  157. package/dist/fx/water-wgsl.d.ts +34 -0
  158. package/dist/fx/water-wgsl.js +197 -0
  159. package/dist/fx/water.d.ts +176 -0
  160. package/dist/fx/water.js +371 -0
  161. package/dist/geom/collide/bvh.d.ts +151 -0
  162. package/dist/geom/collide/bvh.js +493 -0
  163. package/dist/geom/collide/debug.d.ts +14 -0
  164. package/dist/geom/collide/debug.js +74 -0
  165. package/dist/geom/collide/grid.d.ts +26 -0
  166. package/dist/geom/collide/grid.js +91 -0
  167. package/dist/geom/collide/index.d.ts +8 -0
  168. package/dist/geom/collide/index.js +8 -0
  169. package/dist/geom/collide/queries.d.ts +70 -0
  170. package/dist/geom/collide/queries.js +583 -0
  171. package/dist/geom/collide/serialize.d.ts +14 -0
  172. package/dist/geom/collide/serialize.js +145 -0
  173. package/dist/geom/collide/shapes.d.ts +161 -0
  174. package/dist/geom/collide/shapes.js +686 -0
  175. package/dist/geom/collide/slide.d.ts +66 -0
  176. package/dist/geom/collide/slide.js +375 -0
  177. package/dist/geom/collide/tile-slide.d.ts +88 -0
  178. package/dist/geom/collide/tile-slide.js +255 -0
  179. package/dist/geom/geometry.d.ts +95 -0
  180. package/dist/geom/geometry.js +322 -0
  181. package/dist/geom/heightfield.d.ts +72 -0
  182. package/dist/geom/heightfield.js +295 -0
  183. package/dist/geom/hull.d.ts +27 -0
  184. package/dist/geom/hull.js +279 -0
  185. package/dist/geom/index.d.ts +35 -0
  186. package/dist/geom/index.js +23 -0
  187. package/dist/geom/iso.d.ts +74 -0
  188. package/dist/geom/iso.js +89 -0
  189. package/dist/geom/lod.d.ts +29 -0
  190. package/dist/geom/lod.js +27 -0
  191. package/dist/geom/ops.d.ts +47 -0
  192. package/dist/geom/ops.js +285 -0
  193. package/dist/geom/primitives.d.ts +97 -0
  194. package/dist/geom/primitives.js +359 -0
  195. package/dist/geom/shapes.d.ts +43 -0
  196. package/dist/geom/shapes.js +88 -0
  197. package/dist/geom/simplify.d.ts +38 -0
  198. package/dist/geom/simplify.js +519 -0
  199. package/dist/geom/split.d.ts +37 -0
  200. package/dist/geom/split.js +413 -0
  201. package/dist/geom/sweep.d.ts +83 -0
  202. package/dist/geom/sweep.js +534 -0
  203. package/dist/geom/tangents.d.ts +16 -0
  204. package/dist/geom/tangents.js +124 -0
  205. package/dist/geom/triangulate.d.ts +19 -0
  206. package/dist/geom/triangulate.js +147 -0
  207. package/dist/host/default.d.ts +10 -0
  208. package/dist/host/default.js +10 -0
  209. package/dist/host/index.d.ts +4 -0
  210. package/dist/host/index.js +3 -0
  211. package/dist/host/types.d.ts +144 -0
  212. package/dist/host/types.js +1 -0
  213. package/dist/host/web-links.d.ts +38 -0
  214. package/dist/host/web-links.js +110 -0
  215. package/dist/host/web.d.ts +30 -0
  216. package/dist/host/web.js +216 -0
  217. package/dist/host/xr.d.ts +24 -0
  218. package/dist/host/xr.js +59 -0
  219. package/dist/i18n/i18n.d.ts +59 -0
  220. package/dist/i18n/i18n.js +241 -0
  221. package/dist/i18n/index.d.ts +3 -0
  222. package/dist/i18n/index.js +3 -0
  223. package/dist/i18n/message.d.ts +72 -0
  224. package/dist/i18n/message.js +369 -0
  225. package/dist/i18n/negotiate.d.ts +17 -0
  226. package/dist/i18n/negotiate.js +58 -0
  227. package/dist/index.d.ts +7 -0
  228. package/dist/index.js +1 -0
  229. package/dist/input/bindings.d.ts +31 -0
  230. package/dist/input/bindings.js +141 -0
  231. package/dist/input/clock.d.ts +44 -0
  232. package/dist/input/clock.js +49 -0
  233. package/dist/input/follow.d.ts +133 -0
  234. package/dist/input/follow.js +218 -0
  235. package/dist/input/gestures.d.ts +281 -0
  236. package/dist/input/gestures.js +662 -0
  237. package/dist/input/glyphs.d.ts +35 -0
  238. package/dist/input/glyphs.js +216 -0
  239. package/dist/input/gyro.d.ts +85 -0
  240. package/dist/input/gyro.js +140 -0
  241. package/dist/input/index.d.ts +19 -0
  242. package/dist/input/index.js +16 -0
  243. package/dist/input/input.d.ts +543 -0
  244. package/dist/input/input.js +1492 -0
  245. package/dist/input/look.d.ts +327 -0
  246. package/dist/input/look.js +681 -0
  247. package/dist/input/magnify.d.ts +177 -0
  248. package/dist/input/magnify.js +131 -0
  249. package/dist/input/pointer-dispatch.d.ts +138 -0
  250. package/dist/input/pointer-dispatch.js +180 -0
  251. package/dist/input/pointer-router.d.ts +204 -0
  252. package/dist/input/pointer-router.js +533 -0
  253. package/dist/input/pointer-sources.d.ts +164 -0
  254. package/dist/input/pointer-sources.js +306 -0
  255. package/dist/input/rays.d.ts +138 -0
  256. package/dist/input/rays.js +615 -0
  257. package/dist/input/record.d.ts +209 -0
  258. package/dist/input/record.js +601 -0
  259. package/dist/input/touch-zones.d.ts +147 -0
  260. package/dist/input/touch-zones.js +300 -0
  261. package/dist/input/types.d.ts +158 -0
  262. package/dist/input/types.js +1 -0
  263. package/dist/input/xr-source.d.ts +53 -0
  264. package/dist/input/xr-source.js +164 -0
  265. package/dist/nav/astar.d.ts +45 -0
  266. package/dist/nav/astar.js +182 -0
  267. package/dist/nav/corridor.d.ts +40 -0
  268. package/dist/nav/corridor.js +95 -0
  269. package/dist/nav/crowd.d.ts +94 -0
  270. package/dist/nav/crowd.js +624 -0
  271. package/dist/nav/flow.d.ts +61 -0
  272. package/dist/nav/flow.js +193 -0
  273. package/dist/nav/grid.d.ts +108 -0
  274. package/dist/nav/grid.js +210 -0
  275. package/dist/nav/heap.d.ts +22 -0
  276. package/dist/nav/heap.js +92 -0
  277. package/dist/nav/index.d.ts +37 -0
  278. package/dist/nav/index.js +19 -0
  279. package/dist/nav/mesh/areas.d.ts +44 -0
  280. package/dist/nav/mesh/areas.js +98 -0
  281. package/dist/nav/mesh/bake-options.d.ts +33 -0
  282. package/dist/nav/mesh/bake-options.js +76 -0
  283. package/dist/nav/mesh/contour.d.ts +22 -0
  284. package/dist/nav/mesh/contour.js +323 -0
  285. package/dist/nav/mesh/flow3.d.ts +35 -0
  286. package/dist/nav/mesh/flow3.js +229 -0
  287. package/dist/nav/mesh/links.d.ts +38 -0
  288. package/dist/nav/mesh/links.js +72 -0
  289. package/dist/nav/mesh/mesh-path.d.ts +152 -0
  290. package/dist/nav/mesh/mesh-path.js +643 -0
  291. package/dist/nav/mesh/mesh-reach.d.ts +78 -0
  292. package/dist/nav/mesh/mesh-reach.js +450 -0
  293. package/dist/nav/mesh/navmesh.d.ts +117 -0
  294. package/dist/nav/mesh/navmesh.js +647 -0
  295. package/dist/nav/mesh/polymesh.d.ts +24 -0
  296. package/dist/nav/mesh/polymesh.js +384 -0
  297. package/dist/nav/mesh/regions.d.ts +12 -0
  298. package/dist/nav/mesh/regions.js +367 -0
  299. package/dist/nav/mesh/serialize.d.ts +13 -0
  300. package/dist/nav/mesh/serialize.js +197 -0
  301. package/dist/nav/mesh/voxelize.d.ts +111 -0
  302. package/dist/nav/mesh/voxelize.js +552 -0
  303. package/dist/nav/obstacles.d.ts +75 -0
  304. package/dist/nav/obstacles.js +294 -0
  305. package/dist/nav/path.d.ts +59 -0
  306. package/dist/nav/path.js +177 -0
  307. package/dist/nav/query-queue.d.ts +46 -0
  308. package/dist/nav/query-queue.js +308 -0
  309. package/dist/nav/reach.d.ts +53 -0
  310. package/dist/nav/reach.js +148 -0
  311. package/dist/nav/steer.d.ts +36 -0
  312. package/dist/nav/steer.js +78 -0
  313. package/dist/platform/boot.d.ts +28 -0
  314. package/dist/platform/boot.js +43 -0
  315. package/dist/platform/device-mode.d.ts +40 -0
  316. package/dist/platform/device-mode.js +104 -0
  317. package/dist/platform/device.d.ts +31 -0
  318. package/dist/platform/device.js +91 -0
  319. package/dist/platform/environment.d.ts +42 -0
  320. package/dist/platform/environment.js +82 -0
  321. package/dist/platform/gpu.d.ts +40 -0
  322. package/dist/platform/gpu.js +57 -0
  323. package/dist/platform/index.d.ts +18 -0
  324. package/dist/platform/index.js +10 -0
  325. package/dist/platform/letterbox.d.ts +43 -0
  326. package/dist/platform/letterbox.js +57 -0
  327. package/dist/platform/links.d.ts +20 -0
  328. package/dist/platform/links.js +43 -0
  329. package/dist/platform/screens.d.ts +36 -0
  330. package/dist/platform/screens.js +138 -0
  331. package/dist/platform/surface.d.ts +72 -0
  332. package/dist/platform/surface.js +157 -0
  333. package/dist/platform/xr-fake.d.ts +168 -0
  334. package/dist/platform/xr-fake.js +434 -0
  335. package/dist/platform/xr-surface.d.ts +120 -0
  336. package/dist/platform/xr-surface.js +214 -0
  337. package/dist/platform/xr-types.d.ts +262 -0
  338. package/dist/post/ao.d.ts +12 -0
  339. package/dist/post/ao.js +136 -0
  340. package/dist/post/blend.d.ts +27 -0
  341. package/dist/post/blend.js +129 -0
  342. package/dist/post/depth-pass.d.ts +43 -0
  343. package/dist/post/depth-pass.js +118 -0
  344. package/dist/post/dof.d.ts +1 -0
  345. package/dist/post/dof.js +66 -0
  346. package/dist/post/exposure.d.ts +80 -0
  347. package/dist/post/exposure.js +322 -0
  348. package/dist/post/index.d.ts +12 -0
  349. package/dist/post/index.js +11 -0
  350. package/dist/post/lens-dirt.d.ts +13 -0
  351. package/dist/post/lens-dirt.js +91 -0
  352. package/dist/post/lut.d.ts +19 -0
  353. package/dist/post/lut.js +50 -0
  354. package/dist/post/motion-blur.d.ts +9 -0
  355. package/dist/post/motion-blur.js +78 -0
  356. package/dist/post/post-stack.d.ts +302 -0
  357. package/dist/post/post-stack.js +1137 -0
  358. package/dist/post/settings.d.ts +485 -0
  359. package/dist/post/settings.js +416 -0
  360. package/dist/post/shaders.d.ts +25 -0
  361. package/dist/post/shaders.js +410 -0
  362. package/dist/post/ssr.d.ts +16 -0
  363. package/dist/post/ssr.js +155 -0
  364. package/dist/post/taa.d.ts +74 -0
  365. package/dist/post/taa.js +235 -0
  366. package/dist/post/white-balance.d.ts +25 -0
  367. package/dist/post/white-balance.js +98 -0
  368. package/dist/render/bindings.d.ts +98 -0
  369. package/dist/render/bindings.js +281 -0
  370. package/dist/render/blend.d.ts +13 -0
  371. package/dist/render/blend.js +26 -0
  372. package/dist/render/camera-2d.d.ts +72 -0
  373. package/dist/render/camera-2d.js +92 -0
  374. package/dist/render/camera.d.ts +302 -0
  375. package/dist/render/camera.js +671 -0
  376. package/dist/render/color.d.ts +16 -0
  377. package/dist/render/color.js +41 -0
  378. package/dist/render/compressed.d.ts +104 -0
  379. package/dist/render/compressed.js +248 -0
  380. package/dist/render/diagnostics.d.ts +15 -0
  381. package/dist/render/diagnostics.js +43 -0
  382. package/dist/render/dynamic-resolution.d.ts +70 -0
  383. package/dist/render/dynamic-resolution.js +126 -0
  384. package/dist/render/fake-gpu.d.ts +204 -0
  385. package/dist/render/fake-gpu.js +426 -0
  386. package/dist/render/flipbook.d.ts +56 -0
  387. package/dist/render/flipbook.js +87 -0
  388. package/dist/render/frame.d.ts +278 -0
  389. package/dist/render/frame.js +174 -0
  390. package/dist/render/fullscreen.d.ts +54 -0
  391. package/dist/render/fullscreen.js +91 -0
  392. package/dist/render/gpu-timer.d.ts +73 -0
  393. package/dist/render/gpu-timer.js +186 -0
  394. package/dist/render/index.d.ts +53 -0
  395. package/dist/render/index.js +29 -0
  396. package/dist/render/instance-list.d.ts +185 -0
  397. package/dist/render/instance-list.js +547 -0
  398. package/dist/render/lights.d.ts +181 -0
  399. package/dist/render/lights.js +233 -0
  400. package/dist/render/lod.d.ts +54 -0
  401. package/dist/render/lod.js +52 -0
  402. package/dist/render/material.d.ts +165 -0
  403. package/dist/render/material.js +292 -0
  404. package/dist/render/mesh.d.ts +284 -0
  405. package/dist/render/mesh.js +472 -0
  406. package/dist/render/mipmap.d.ts +16 -0
  407. package/dist/render/mipmap.js +121 -0
  408. package/dist/render/parallax.d.ts +50 -0
  409. package/dist/render/parallax.js +47 -0
  410. package/dist/render/pbr-wgsl.d.ts +31 -0
  411. package/dist/render/pbr-wgsl.js +119 -0
  412. package/dist/render/ping-pong.d.ts +42 -0
  413. package/dist/render/ping-pong.js +74 -0
  414. package/dist/render/renderer.d.ts +741 -0
  415. package/dist/render/renderer.js +2486 -0
  416. package/dist/render/scene-pipelines.d.ts +44 -0
  417. package/dist/render/scene-pipelines.js +92 -0
  418. package/dist/render/scene-target.d.ts +170 -0
  419. package/dist/render/scene-target.js +450 -0
  420. package/dist/render/shadow.d.ts +74 -0
  421. package/dist/render/shadow.js +208 -0
  422. package/dist/render/skin.d.ts +112 -0
  423. package/dist/render/skin.js +240 -0
  424. package/dist/render/snapshot.d.ts +36 -0
  425. package/dist/render/snapshot.js +34 -0
  426. package/dist/render/standard.d.ts +162 -0
  427. package/dist/render/standard.js +263 -0
  428. package/dist/render/texture.d.ts +142 -0
  429. package/dist/render/texture.js +228 -0
  430. package/dist/render/tile-layer.d.ts +110 -0
  431. package/dist/render/tile-layer.js +304 -0
  432. package/dist/render/transform.d.ts +115 -0
  433. package/dist/render/transform.js +318 -0
  434. package/dist/render/wgsl.d.ts +131 -0
  435. package/dist/render/wgsl.js +528 -0
  436. package/dist/save/codec.d.ts +49 -0
  437. package/dist/save/codec.js +169 -0
  438. package/dist/save/index.d.ts +8 -0
  439. package/dist/save/index.js +8 -0
  440. package/dist/save/journal.d.ts +24 -0
  441. package/dist/save/journal.js +102 -0
  442. package/dist/save/migrate.d.ts +15 -0
  443. package/dist/save/migrate.js +37 -0
  444. package/dist/save/schema.d.ts +72 -0
  445. package/dist/save/schema.js +154 -0
  446. package/dist/save/settings.d.ts +28 -0
  447. package/dist/save/settings.js +59 -0
  448. package/dist/save/slots.d.ts +56 -0
  449. package/dist/save/slots.js +200 -0
  450. package/dist/save/storage.d.ts +35 -0
  451. package/dist/save/storage.js +62 -0
  452. package/dist/save/store.d.ts +114 -0
  453. package/dist/save/store.js +269 -0
  454. package/dist/testing/fake-host.d.ts +103 -0
  455. package/dist/testing/fake-host.js +163 -0
  456. package/dist/testing/fixed-clock.d.ts +25 -0
  457. package/dist/testing/fixed-clock.js +30 -0
  458. package/dist/testing/index.d.ts +4 -0
  459. package/dist/testing/index.js +5 -0
  460. package/dist/testing/test-game.d.ts +76 -0
  461. package/dist/testing/test-game.js +92 -0
  462. package/dist/tween/ease.d.ts +77 -0
  463. package/dist/tween/ease.js +192 -0
  464. package/dist/tween/index.d.ts +8 -0
  465. package/dist/tween/index.js +7 -0
  466. package/dist/tween/timeline.d.ts +78 -0
  467. package/dist/tween/timeline.js +269 -0
  468. package/dist/tween/tween.d.ts +168 -0
  469. package/dist/tween/tween.js +320 -0
  470. package/dist/ui/anchor.d.ts +206 -0
  471. package/dist/ui/anchor.js +307 -0
  472. package/dist/ui/caption.d.ts +122 -0
  473. package/dist/ui/caption.js +286 -0
  474. package/dist/ui/cursor.d.ts +105 -0
  475. package/dist/ui/cursor.js +261 -0
  476. package/dist/ui/dom.d.ts +30 -0
  477. package/dist/ui/dom.js +92 -0
  478. package/dist/ui/floats.d.ts +86 -0
  479. package/dist/ui/floats.js +260 -0
  480. package/dist/ui/hud.d.ts +198 -0
  481. package/dist/ui/hud.js +431 -0
  482. package/dist/ui/index.d.ts +37 -0
  483. package/dist/ui/index.js +34 -0
  484. package/dist/ui/inventory-card.d.ts +52 -0
  485. package/dist/ui/inventory-card.js +173 -0
  486. package/dist/ui/inventory-context.d.ts +29 -0
  487. package/dist/ui/inventory-context.js +125 -0
  488. package/dist/ui/inventory-drag.d.ts +63 -0
  489. package/dist/ui/inventory-drag.js +394 -0
  490. package/dist/ui/inventory-grid.d.ts +66 -0
  491. package/dist/ui/inventory-grid.js +625 -0
  492. package/dist/ui/inventory-radial.d.ts +39 -0
  493. package/dist/ui/inventory-radial.js +168 -0
  494. package/dist/ui/inventory.d.ts +128 -0
  495. package/dist/ui/inventory.js +154 -0
  496. package/dist/ui/layout.d.ts +356 -0
  497. package/dist/ui/layout.js +856 -0
  498. package/dist/ui/link.d.ts +64 -0
  499. package/dist/ui/link.js +155 -0
  500. package/dist/ui/loading.d.ts +40 -0
  501. package/dist/ui/loading.js +157 -0
  502. package/dist/ui/loupe.d.ts +74 -0
  503. package/dist/ui/loupe.js +164 -0
  504. package/dist/ui/menu-nav.d.ts +33 -0
  505. package/dist/ui/menu-nav.js +28 -0
  506. package/dist/ui/minimap-canvas.d.ts +145 -0
  507. package/dist/ui/minimap-canvas.js +460 -0
  508. package/dist/ui/notify.d.ts +40 -0
  509. package/dist/ui/notify.js +141 -0
  510. package/dist/ui/orb.d.ts +53 -0
  511. package/dist/ui/orb.js +304 -0
  512. package/dist/ui/overlay.d.ts +213 -0
  513. package/dist/ui/overlay.js +394 -0
  514. package/dist/ui/padnav.d.ts +53 -0
  515. package/dist/ui/padnav.js +146 -0
  516. package/dist/ui/perf.d.ts +130 -0
  517. package/dist/ui/perf.js +329 -0
  518. package/dist/ui/rotate-card.d.ts +54 -0
  519. package/dist/ui/rotate-card.js +97 -0
  520. package/dist/ui/stack.d.ts +128 -0
  521. package/dist/ui/stack.js +312 -0
  522. package/dist/ui/styles.d.ts +13 -0
  523. package/dist/ui/styles.js +200 -0
  524. package/dist/ui/text-scale.d.ts +67 -0
  525. package/dist/ui/text-scale.js +102 -0
  526. package/dist/ui/theme.d.ts +70 -0
  527. package/dist/ui/theme.js +98 -0
  528. package/dist/ui/toast.d.ts +23 -0
  529. package/dist/ui/toast.js +46 -0
  530. package/dist/ui/tooltip.d.ts +89 -0
  531. package/dist/ui/tooltip.js +333 -0
  532. package/dist/ui/widgets-2.d.ts +134 -0
  533. package/dist/ui/widgets-2.js +399 -0
  534. package/dist/ui/widgets-3.d.ts +91 -0
  535. package/dist/ui/widgets-3.js +212 -0
  536. package/dist/ui/widgets.d.ts +34 -0
  537. package/dist/ui/widgets.js +50 -0
  538. package/dist/ui/world-panel/index.d.ts +5 -0
  539. package/dist/ui/world-panel/index.js +5 -0
  540. package/dist/ui/world-panel/layout.d.ts +38 -0
  541. package/dist/ui/world-panel/layout.js +68 -0
  542. package/dist/ui/world-panel/paint.d.ts +96 -0
  543. package/dist/ui/world-panel/paint.js +136 -0
  544. package/dist/ui/world-panel/panel.d.ts +235 -0
  545. package/dist/ui/world-panel/panel.js +762 -0
  546. package/dist/ui/world-panel/wgsl.d.ts +10 -0
  547. package/dist/ui/world-panel/wgsl.js +52 -0
  548. package/dist/ui/world-panel/widgets.d.ts +207 -0
  549. package/dist/ui/world-panel/widgets.js +569 -0
  550. package/dist/voxel/chunks.d.ts +77 -0
  551. package/dist/voxel/chunks.js +149 -0
  552. package/dist/voxel/grid.d.ts +84 -0
  553. package/dist/voxel/grid.js +223 -0
  554. package/dist/voxel/index.d.ts +22 -0
  555. package/dist/voxel/index.js +7 -0
  556. package/dist/voxel/mesher-worker.d.ts +14 -0
  557. package/dist/voxel/mesher-worker.js +47 -0
  558. package/dist/voxel/mesher.d.ts +73 -0
  559. package/dist/voxel/mesher.js +306 -0
  560. package/dist/voxel/ray.d.ts +32 -0
  561. package/dist/voxel/ray.js +73 -0
  562. package/dist/voxel/wgsl.d.ts +16 -0
  563. package/dist/voxel/wgsl.js +28 -0
  564. package/dist/voxel/worker-client.d.ts +79 -0
  565. package/dist/voxel/worker-client.js +122 -0
  566. package/package.json +177 -3
@@ -0,0 +1,339 @@
1
+ /**
2
+ * Clip compression: fewer keyframes and smaller ones, so a wardrobe of animations fits.
3
+ *
4
+ * `compressClip` keeps, per channel, only the keys a linear fit cannot reproduce within a
5
+ * tolerance (Ramer–Douglas–Peucker over the sampled curve: start from the two end keys, add
6
+ * the key with the largest error until every dropped key is within tolerance), collapses a
7
+ * channel whose keys never change to one key and drops it entirely when that value is the
8
+ * skeleton's rest (with `skeleton`). `quantizeClip` packs rotation keys into three `uint16`
9
+ * per key (smallest-three, `AnimationChannel.packed`), which `sampleChannel` reads directly.
10
+ * Both allocate (they build new clips); call them at load or in a bake step. `CUBICSPLINE`
11
+ * channels are copied unchanged by both.
12
+ */
13
+ import { channelComponents, unpackQuat } from './clip.js';
14
+ import { POSE_NODE_FLOATS } from './pose.js';
15
+ /** Keyframe bytes of a clip: `times`, `values` and `packed` over every channel. */
16
+ export function clipBytes(clip) {
17
+ let bytes = 0;
18
+ for (const ch of clip.channels) {
19
+ bytes += ch.times.byteLength + ch.values.byteLength + (ch.packed?.byteLength ?? 0);
20
+ }
21
+ return bytes;
22
+ }
23
+ const qa = new Float32Array(4);
24
+ const qb = new Float32Array(4);
25
+ /**
26
+ * The angle between two unit quaternions at `values[a..]` and `values[b..]`, from the chord
27
+ * (`2·asin(|q₁ ∓ q₂| / 2)`), which keeps its precision near zero where `acos(dot)` does not.
28
+ */
29
+ function quatAngle(values, a, b) {
30
+ const dot = values[a] * values[b] +
31
+ values[a + 1] * values[b + 1] +
32
+ values[a + 2] * values[b + 2] +
33
+ values[a + 3] * values[b + 3];
34
+ const s = dot < 0 ? -1 : 1;
35
+ const dx = values[a] - s * values[b];
36
+ const dy = values[a + 1] - s * values[b + 1];
37
+ const dz = values[a + 2] - s * values[b + 2];
38
+ const dw = values[a + 3] - s * values[b + 3];
39
+ return 2 * Math.asin(Math.min(1, Math.hypot(dx, dy, dz, dw) / 2));
40
+ }
41
+ /** Largest absolute component gap between keys `a` and `b` (`comps` each). */
42
+ function maxGap(values, a, b, comps) {
43
+ let m = 0;
44
+ for (let i = 0; i < comps; i++)
45
+ m = Math.max(m, Math.abs(values[a + i] - values[b + i]));
46
+ return m;
47
+ }
48
+ /**
49
+ * The error of key `k` against the linear (slerp for rotations) fit between keys `i` and `j`.
50
+ */
51
+ function fitError(ch, comps, i, j, k) {
52
+ const times = ch.times;
53
+ const values = ch.values;
54
+ const span = times[j] - times[i];
55
+ const u = span > 0 ? (times[k] - times[i]) / span : 0;
56
+ const rotation = ch.path === 'rotation';
57
+ if (ch.interpolation === 'STEP') {
58
+ // A step channel holds key i until key j: the dropped key must equal key i.
59
+ return rotation
60
+ ? quatAngle(values, i * comps, k * comps)
61
+ : maxGap(values, i * comps, k * comps, comps);
62
+ }
63
+ if (rotation) {
64
+ slerpInto(qa, values, i * comps, values, j * comps, u);
65
+ for (let c = 0; c < 4; c++)
66
+ qb[c] = values[k * comps + c];
67
+ return angleBetween(qa, qb);
68
+ }
69
+ let m = 0;
70
+ for (let c = 0; c < comps; c++) {
71
+ const fit = (1 - u) * values[i * comps + c] + u * values[j * comps + c];
72
+ m = Math.max(m, Math.abs(fit - values[k * comps + c]));
73
+ }
74
+ return m;
75
+ }
76
+ function angleBetween(a, b) {
77
+ for (let i = 0; i < 4; i++)
78
+ qab[i] = a[i];
79
+ for (let i = 0; i < 4; i++)
80
+ qab[4 + i] = b[i];
81
+ return quatAngle(qab, 0, 4);
82
+ }
83
+ const qab = new Float64Array(8);
84
+ /** Shortest-arc slerp of two quaternions (no dependence on `quat.ts` scratch). */
85
+ function slerpInto(out, a, ao, b, bo, t) {
86
+ let bx = b[bo];
87
+ let by = b[bo + 1];
88
+ let bz = b[bo + 2];
89
+ let bw = b[bo + 3];
90
+ let cos = a[ao] * bx + a[ao + 1] * by + a[ao + 2] * bz + a[ao + 3] * bw;
91
+ if (cos < 0) {
92
+ cos = -cos;
93
+ bx = -bx;
94
+ by = -by;
95
+ bz = -bz;
96
+ bw = -bw;
97
+ }
98
+ let s0;
99
+ let s1;
100
+ if (cos > 0.9995) {
101
+ s0 = 1 - t;
102
+ s1 = t;
103
+ }
104
+ else {
105
+ const theta = Math.acos(cos);
106
+ const sin = Math.sin(theta);
107
+ s0 = Math.sin((1 - t) * theta) / sin;
108
+ s1 = Math.sin(t * theta) / sin;
109
+ }
110
+ out[0] = s0 * a[ao] + s1 * bx;
111
+ out[1] = s0 * a[ao + 1] + s1 * by;
112
+ out[2] = s0 * a[ao + 2] + s1 * bz;
113
+ out[3] = s0 * a[ao + 3] + s1 * bw;
114
+ const len = Math.hypot(out[0], out[1], out[2], out[3]) || 1;
115
+ for (let i = 0; i < 4; i++)
116
+ out[i] = out[i] / len;
117
+ }
118
+ function toleranceOf(ch, opts) {
119
+ switch (ch.path) {
120
+ case 'translation':
121
+ return opts.positionError ?? 1e-3;
122
+ case 'rotation':
123
+ return opts.rotationError ?? 1e-3;
124
+ case 'scale':
125
+ return opts.scaleError ?? 1e-3;
126
+ default:
127
+ return opts.weightError ?? 1e-3;
128
+ }
129
+ }
130
+ /** Keys of `ch` (STEP or LINEAR) to keep, ascending, by RDP within `tol`. */
131
+ function keepKeys(ch, comps, tol) {
132
+ const n = ch.times.length;
133
+ if (n <= 2) {
134
+ const all = [];
135
+ for (let i = 0; i < n; i++)
136
+ all.push(i);
137
+ return all;
138
+ }
139
+ const keep = new Uint8Array(n);
140
+ keep[0] = 1;
141
+ keep[n - 1] = 1;
142
+ const stack = [0, n - 1];
143
+ while (stack.length > 0) {
144
+ const j = stack.pop();
145
+ const i = stack.pop();
146
+ if (j - i < 2)
147
+ continue;
148
+ let worst = -1;
149
+ let worstErr = tol;
150
+ for (let k = i + 1; k < j; k++) {
151
+ const e = fitError(ch, comps, i, j, k);
152
+ if (e > worstErr) {
153
+ worstErr = e;
154
+ worst = k;
155
+ }
156
+ }
157
+ if (worst < 0)
158
+ continue;
159
+ keep[worst] = 1;
160
+ stack.push(i, worst, worst, j);
161
+ }
162
+ const out = [];
163
+ for (let i = 0; i < n; i++)
164
+ if (keep[i])
165
+ out.push(i);
166
+ return out;
167
+ }
168
+ /** Whether every key of `ch` equals its first within `tol`. */
169
+ function isConstant(ch, comps, tol) {
170
+ const n = ch.times.length;
171
+ for (let k = 1; k < n; k++) {
172
+ const e = ch.path === 'rotation'
173
+ ? quatAngle(ch.values, 0, k * comps)
174
+ : maxGap(ch.values, 0, k * comps, comps);
175
+ if (e > tol)
176
+ return false;
177
+ }
178
+ return true;
179
+ }
180
+ /** Whether key 0 of `ch` is the node's rest value within `tol`. */
181
+ function isRest(ch, tol, skeleton) {
182
+ const n = skeleton.parents.length;
183
+ if (ch.node < 0 || ch.node >= n)
184
+ return false;
185
+ const o = ch.node * POSE_NODE_FLOATS;
186
+ const rest = skeleton.rest;
187
+ switch (ch.path) {
188
+ case 'translation':
189
+ return (Math.abs(ch.values[0] - rest[o]) <= tol &&
190
+ Math.abs(ch.values[1] - rest[o + 1]) <= tol &&
191
+ Math.abs(ch.values[2] - rest[o + 2]) <= tol);
192
+ case 'rotation': {
193
+ for (let i = 0; i < 4; i++)
194
+ qa[i] = rest[o + 3 + i];
195
+ for (let i = 0; i < 4; i++)
196
+ qb[i] = ch.values[i];
197
+ return angleBetween(qa, qb) <= tol;
198
+ }
199
+ case 'scale':
200
+ return (Math.abs(ch.values[0] - rest[o + 7]) <= tol &&
201
+ Math.abs(ch.values[1] - rest[o + 8]) <= tol &&
202
+ Math.abs(ch.values[2] - rest[o + 9]) <= tol);
203
+ default:
204
+ return false;
205
+ }
206
+ }
207
+ /**
208
+ * A copy of `clip` with the keys a linear fit reproduces within tolerance removed, constant
209
+ * channels collapsed (and dropped when they equal the rest of `skeleton`), and the keyframe
210
+ * byte counts before and after. Packed channels are decoded first; `CUBICSPLINE` channels and
211
+ * events are copied as they are. Allocates.
212
+ */
213
+ export function compressClip(clip, opts = {}) {
214
+ const dropConstant = opts.dropConstant ?? true;
215
+ const channels = [];
216
+ for (const src of clip.channels) {
217
+ const ch = src.packed ? unpackChannel(src) : src;
218
+ const comps = channelComponents(ch);
219
+ const n = ch.times.length;
220
+ if (ch.interpolation === 'CUBICSPLINE' || n === 0 || comps === 0) {
221
+ channels.push({ ...ch, times: ch.times.slice(), values: ch.values.slice() });
222
+ continue;
223
+ }
224
+ const tol = toleranceOf(ch, opts);
225
+ if (dropConstant && isConstant(ch, comps, tol)) {
226
+ if (opts.skeleton && isRest(ch, tol, opts.skeleton))
227
+ continue;
228
+ channels.push({
229
+ node: ch.node,
230
+ path: ch.path,
231
+ interpolation: 'STEP',
232
+ times: new Float32Array([ch.times[0]]),
233
+ values: ch.values.slice(0, comps),
234
+ });
235
+ continue;
236
+ }
237
+ const keys = keepKeys(ch, comps, tol);
238
+ const times = new Float32Array(keys.length);
239
+ const values = new Float32Array(keys.length * comps);
240
+ for (let i = 0; i < keys.length; i++) {
241
+ const k = keys[i];
242
+ times[i] = ch.times[k];
243
+ for (let c = 0; c < comps; c++)
244
+ values[i * comps + c] = ch.values[k * comps + c];
245
+ }
246
+ channels.push({ node: ch.node, path: ch.path, interpolation: ch.interpolation, times, values });
247
+ }
248
+ const out = { name: clip.name, duration: clip.duration, channels };
249
+ if (clip.events)
250
+ out.events = clip.events.map((e) => ({ ...e }));
251
+ return { clip: out, before: clipBytes(clip), after: clipBytes(out) };
252
+ }
253
+ /** A float copy of a packed rotation channel. */
254
+ function unpackChannel(ch) {
255
+ const n = ch.times.length;
256
+ const values = new Float32Array(n * 4);
257
+ for (let k = 0; k < n; k++)
258
+ unpackQuat(ch.packed, k, values, k * 4);
259
+ return { node: ch.node, path: ch.path, interpolation: ch.interpolation, times: ch.times, values };
260
+ }
261
+ const PACK_SCALE = 32767 / Math.SQRT2;
262
+ /**
263
+ * Encode the unit quaternion at `q[o..o+4)` as three `uint16` at `out[b..b+3)` in the
264
+ * smallest-three form `unpackQuat` decodes: the quaternion is flipped so its largest
265
+ * component is positive, that component's index goes to bit 15 of words 0 and 1, and the
266
+ * other three components, each within ±1/√2, fill the low 15 bits of the words in order.
267
+ */
268
+ export function packQuat(q, o, out, b) {
269
+ let largest = 0;
270
+ let mag = 0;
271
+ for (let i = 0; i < 4; i++) {
272
+ const a = Math.abs(q[o + i]);
273
+ if (a > mag) {
274
+ mag = a;
275
+ largest = i;
276
+ }
277
+ }
278
+ const sign = q[o + largest] < 0 ? -1 : 1;
279
+ let w = 0;
280
+ for (let i = 0; i < 4; i++) {
281
+ if (i === largest)
282
+ continue;
283
+ let c = q[o + i] * sign;
284
+ c = Math.min(Math.SQRT1_2, Math.max(-Math.SQRT1_2, c));
285
+ let bits = Math.round((c + Math.SQRT1_2) * PACK_SCALE);
286
+ bits = Math.min(0x7fff, Math.max(0, bits));
287
+ if (w === 0)
288
+ bits |= (largest >> 1) << 15;
289
+ else if (w === 1)
290
+ bits |= (largest & 1) << 15;
291
+ out[b + w] = bits;
292
+ w++;
293
+ }
294
+ }
295
+ /**
296
+ * A copy of `clip` whose `STEP`/`LINEAR` rotation channels carry `packed` keys (three
297
+ * `uint16` each, `packQuat`) and an empty `values`; other channels are copied. `'none'`
298
+ * copies everything. Allocates.
299
+ */
300
+ export function quantizeClip(clip, opts) {
301
+ const channels = [];
302
+ for (const src of clip.channels) {
303
+ const packable = opts.rotation === 'smallest-three' &&
304
+ src.path === 'rotation' &&
305
+ src.interpolation !== 'CUBICSPLINE';
306
+ if (!packable) {
307
+ const ch = { ...src, times: src.times.slice(), values: src.values.slice() };
308
+ if (src.packed)
309
+ ch.packed = src.packed.slice();
310
+ channels.push(ch);
311
+ continue;
312
+ }
313
+ if (src.packed) {
314
+ channels.push({
315
+ ...src,
316
+ times: src.times.slice(),
317
+ values: new Float32Array(0),
318
+ packed: src.packed.slice(),
319
+ });
320
+ continue;
321
+ }
322
+ const n = src.times.length;
323
+ const packed = new Uint16Array(n * 3);
324
+ for (let k = 0; k < n; k++)
325
+ packQuat(src.values, k * 4, packed, k * 3);
326
+ channels.push({
327
+ node: src.node,
328
+ path: src.path,
329
+ interpolation: src.interpolation,
330
+ times: src.times.slice(),
331
+ values: new Float32Array(0),
332
+ packed,
333
+ });
334
+ }
335
+ const out = { name: clip.name, duration: clip.duration, channels };
336
+ if (clip.events)
337
+ out.events = clip.events.map((e) => ({ ...e }));
338
+ return out;
339
+ }
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Foot placement: a probe (the game's ground query) under each foot, a pelvis that sinks to the
3
+ * lowest foot, two-bone IK per leg to hold the feet on the probed ground and a tilt of each foot
4
+ * onto the ground normal. The animation's floor is the instance origin's plane (`y` of the
5
+ * instance translation); each foot's lift is the probed height above that plane, clamped to
6
+ * `[-maxDrop, maxLift]`. Same conventions as `ik.ts`: `worldPose` is `poseToWorld` of the sampled
7
+ * `pose`, which this rewrites (translations of `pelvis`, rotations of root/mid/tip per foot).
8
+ * Flat ground at the origin plane leaves the pose untouched, bit for bit. Allocates in
9
+ * `createFootIk` (the chains and a world buffer), nothing per frame; the probe must not either.
10
+ */
11
+ import { type IkChain } from './ik.js';
12
+ import { type Pose, type Skeleton } from './pose.js';
13
+ /** One leg: `mid` descends from `root` (hip), `tip` (ankle) from `mid`; `toe` is probed too. */
14
+ export interface FootIkFootDesc {
15
+ root: number;
16
+ mid: number;
17
+ tip: number;
18
+ /** Optional toe joint: the ground is probed under it as well and the higher hit is used. */
19
+ toe?: number;
20
+ /** Skeleton-space direction the knee bends toward when the leg is straight. Default +Z. */
21
+ bend?: ArrayLike<number>;
22
+ }
23
+ /**
24
+ * The ground query, in instance (world) space: writes `[y, nx, ny, nz]` for the ground under
25
+ * `(x, z)` into `out` and returns `true`, or returns `false` for no ground (the foot is left to
26
+ * the animation). It is called once per foot (twice with a toe) per `applyFootIk`.
27
+ */
28
+ export type FootIkProbe = (x: number, z: number, out: Float32Array) => boolean;
29
+ export interface FootIkDesc {
30
+ feet: FootIkFootDesc[];
31
+ probe: FootIkProbe;
32
+ /** The joint whose translation sinks toward the lowest foot (hips). */
33
+ pelvis: number;
34
+ /** Furthest a foot rises above the animation's floor (world units). Default 0.5. */
35
+ maxLift?: number;
36
+ /** Furthest a foot drops below it. Default 0.5. */
37
+ maxDrop?: number;
38
+ /** Pelvis smoothing rate per second (exponential approach). Default 12. */
39
+ blend?: number;
40
+ }
41
+ /** The feet with their scratch, from `createFootIk`. Read `pelvisOffset` and `lift`. */
42
+ export interface FootIk {
43
+ readonly feet: readonly FootIkFootDesc[];
44
+ readonly probe: FootIkProbe;
45
+ readonly pelvis: number;
46
+ readonly maxLift: number;
47
+ readonly maxDrop: number;
48
+ readonly blend: number;
49
+ /** The current pelvis sink (world units, ≤ 0 going down), smoothed. */
50
+ pelvisOffset: number;
51
+ /** Per foot, the clamped ground height above the floor that the foot was placed at. */
52
+ readonly lift: Float32Array;
53
+ /** Per foot, 1 when the probe hit. */
54
+ readonly grounded: Uint8Array;
55
+ /** @internal */
56
+ readonly chains: IkChain[];
57
+ /** @internal per foot, the bend direction (unit, skeleton space). */
58
+ readonly bends: Float32Array;
59
+ /** @internal per foot, the ground normal in skeleton space (unit). */
60
+ readonly normals: Float32Array;
61
+ /** @internal per foot, the sampled tip position in skeleton space. */
62
+ readonly tips: Float32Array;
63
+ /** @internal world matrices of the pose after the pelvis moved. */
64
+ readonly world: Float32Array;
65
+ }
66
+ /** Validate and allocate; call once. */
67
+ export declare function createFootIk(skeleton: Skeleton, desc: FootIkDesc): FootIk;
68
+ /**
69
+ * Probe under every foot, sink the pelvis toward the lowest one (smoothed by `blend` over `dt`),
70
+ * then solve each leg to its ground height and tilt the foot onto the normal. `instanceWorld`
71
+ * (3×4 row-major, or `null` for the identity) places the skeleton in the probe's space. No
72
+ * allocation.
73
+ */
74
+ export declare function applyFootIk(pose: Pose, skeleton: Skeleton, worldPose: Float32Array, footIk: FootIk, instanceWorld: Float32Array | null, dt: number): void;
@@ -0,0 +1,241 @@
1
+ /**
2
+ * Foot placement: a probe (the game's ground query) under each foot, a pelvis that sinks to the
3
+ * lowest foot, two-bone IK per leg to hold the feet on the probed ground and a tilt of each foot
4
+ * onto the ground normal. The animation's floor is the instance origin's plane (`y` of the
5
+ * instance translation); each foot's lift is the probed height above that plane, clamped to
6
+ * `[-maxDrop, maxLift]`. Same conventions as `ik.ts`: `worldPose` is `poseToWorld` of the sampled
7
+ * `pose`, which this rewrites (translations of `pelvis`, rotations of root/mid/tip per foot).
8
+ * Flat ground at the origin plane leaves the pose untouched, bit for bit. Allocates in
9
+ * `createFootIk` (the chains and a world buffer), nothing per frame; the probe must not either.
10
+ */
11
+ import { applyDelta, createIkChain, IDENTITY, normalize, rotationBetween, transformDir, transformPoint, twoBoneIk, } from './ik.js';
12
+ import { invertAffine, multiplyAffine, POSE_NODE_FLOATS, poseToWorld, WORLD_NODE_FLOATS, } from './pose.js';
13
+ const EPS = 1e-6;
14
+ /** Validate and allocate; call once. */
15
+ export function createFootIk(skeleton, desc) {
16
+ const n = skeleton.parents.length;
17
+ if (desc.feet.length === 0)
18
+ throw new Error('FootIk: at least one foot');
19
+ if (desc.pelvis < 0 || desc.pelvis >= n)
20
+ throw new Error(`FootIk: pelvis ${desc.pelvis}`);
21
+ const chains = [];
22
+ const bends = new Float32Array(desc.feet.length * 3);
23
+ for (let i = 0; i < desc.feet.length; i++) {
24
+ const f = desc.feet[i];
25
+ chains.push(createIkChain(skeleton, f));
26
+ if (f.toe !== undefined && (f.toe < 0 || f.toe >= n))
27
+ throw new Error(`FootIk: toe ${f.toe}`);
28
+ const b = f.bend ?? [0, 0, 1];
29
+ bends[i * 3] = b[0];
30
+ bends[i * 3 + 1] = b[1];
31
+ bends[i * 3 + 2] = b[2];
32
+ if (normalize(bends.subarray(i * 3, i * 3 + 3)) <= EPS)
33
+ throw new Error(`FootIk: bend of foot ${i} must be non-zero`);
34
+ }
35
+ return {
36
+ feet: desc.feet,
37
+ probe: desc.probe,
38
+ pelvis: desc.pelvis,
39
+ maxLift: desc.maxLift ?? 0.5,
40
+ maxDrop: desc.maxDrop ?? 0.5,
41
+ blend: desc.blend ?? 12,
42
+ pelvisOffset: 0,
43
+ lift: new Float32Array(desc.feet.length),
44
+ grounded: new Uint8Array(desc.feet.length),
45
+ chains,
46
+ bends,
47
+ normals: new Float32Array(desc.feet.length * 3),
48
+ tips: new Float32Array(desc.feet.length * 3),
49
+ world: new Float32Array(n * WORLD_NODE_FLOATS),
50
+ };
51
+ }
52
+ const hit = new Float32Array(4);
53
+ const inv = new Float32Array(WORLD_NODE_FLOATS);
54
+ const upSk = new Float32Array(3);
55
+ const upN = new Float32Array(3);
56
+ const p = new Float32Array(3);
57
+ const q = new Float32Array(3);
58
+ const nrm = new Float32Array(3);
59
+ const target = new Float32Array(3);
60
+ const pole = new Float32Array(3);
61
+ const delta = new Float32Array(4);
62
+ const undo = new Float32Array(WORLD_NODE_FLOATS);
63
+ const WORLD_UP = new Float32Array([0, 1, 0]);
64
+ /**
65
+ * Probe under every foot, sink the pelvis toward the lowest one (smoothed by `blend` over `dt`),
66
+ * then solve each leg to its ground height and tilt the foot onto the normal. `instanceWorld`
67
+ * (3×4 row-major, or `null` for the identity) places the skeleton in the probe's space. No
68
+ * allocation.
69
+ */
70
+ export function applyFootIk(pose, skeleton, worldPose, footIk, instanceWorld, dt) {
71
+ const feet = footIk.feet;
72
+ const originY = instanceWorld ? instanceWorld[7] : 0;
73
+ if (instanceWorld) {
74
+ invertAffine(inv, 0, instanceWorld, 0);
75
+ transformDir(upSk, inv, 0, WORLD_UP);
76
+ }
77
+ else {
78
+ upSk[0] = 0;
79
+ upSk[1] = 1;
80
+ upSk[2] = 0;
81
+ }
82
+ upN[0] = upSk[0];
83
+ upN[1] = upSk[1];
84
+ upN[2] = upSk[2];
85
+ if (normalize(upN) <= EPS)
86
+ return;
87
+ // Probe. `lowest` is the pelvis target; `flat` stays true while nothing needs to move.
88
+ let lowest = 0;
89
+ let flat = true;
90
+ for (let i = 0; i < feet.length; i++) {
91
+ const f = feet[i];
92
+ const to = f.tip * WORLD_NODE_FLOATS;
93
+ footIk.tips[i * 3] = worldPose[to + 3];
94
+ footIk.tips[i * 3 + 1] = worldPose[to + 7];
95
+ footIk.tips[i * 3 + 2] = worldPose[to + 11];
96
+ footIk.grounded[i] = 0;
97
+ footIk.lift[i] = 0;
98
+ footIk.normals[i * 3] = upN[0];
99
+ footIk.normals[i * 3 + 1] = upN[1];
100
+ footIk.normals[i * 3 + 2] = upN[2];
101
+ let d = Number.NEGATIVE_INFINITY;
102
+ let got = false;
103
+ for (let k = 0; k < 2; k++) {
104
+ const node = k === 0 ? f.tip : f.toe;
105
+ if (node === undefined)
106
+ break;
107
+ const wo = node * WORLD_NODE_FLOATS;
108
+ p[0] = worldPose[wo + 3];
109
+ p[1] = worldPose[wo + 7];
110
+ p[2] = worldPose[wo + 11];
111
+ if (instanceWorld)
112
+ transformPoint(q, instanceWorld, 0, p);
113
+ else
114
+ q.set(p);
115
+ if (!footIk.probe(q[0], q[2], hit))
116
+ continue;
117
+ const h = hit[0] - originY;
118
+ if (!got || h > d) {
119
+ d = h;
120
+ nrm[0] = hit[1];
121
+ nrm[1] = hit[2];
122
+ nrm[2] = hit[3];
123
+ }
124
+ got = true;
125
+ }
126
+ if (!got)
127
+ continue;
128
+ d = Math.min(footIk.maxLift, Math.max(-footIk.maxDrop, d));
129
+ footIk.grounded[i] = 1;
130
+ footIk.lift[i] = d;
131
+ if (instanceWorld)
132
+ transformDir(nrm, inv, 0, nrm);
133
+ if (normalize(nrm) > EPS) {
134
+ footIk.normals[i * 3] = nrm[0];
135
+ footIk.normals[i * 3 + 1] = nrm[1];
136
+ footIk.normals[i * 3 + 2] = nrm[2];
137
+ const dot = nrm[0] * upN[0] + nrm[1] * upN[1] + nrm[2] * upN[2];
138
+ if (dot < 1 - 1e-6)
139
+ flat = false;
140
+ }
141
+ if (d !== 0)
142
+ flat = false;
143
+ if (d < lowest)
144
+ lowest = d;
145
+ }
146
+ // Pelvis: approach the lowest foot exponentially.
147
+ const k = dt > 0 ? 1 - Math.exp(-footIk.blend * dt) : 1;
148
+ footIk.pelvisOffset += (lowest - footIk.pelvisOffset) * k;
149
+ if (Math.abs(footIk.pelvisOffset) < 1e-6)
150
+ footIk.pelvisOffset = 0;
151
+ if (flat && footIk.pelvisOffset === 0)
152
+ return;
153
+ const offset = footIk.pelvisOffset;
154
+ if (offset !== 0) {
155
+ const parent = skeleton.parents[footIk.pelvis];
156
+ p[0] = upSk[0] * offset;
157
+ p[1] = upSk[1] * offset;
158
+ p[2] = upSk[2] * offset;
159
+ if (parent >= 0) {
160
+ invertAffine(inv, 0, worldPose, parent * WORLD_NODE_FLOATS);
161
+ transformDir(p, inv, 0, p);
162
+ }
163
+ const po = footIk.pelvis * POSE_NODE_FLOATS;
164
+ pose[po] = pose[po] + p[0];
165
+ pose[po + 1] = pose[po + 1] + p[1];
166
+ pose[po + 2] = pose[po + 2] + p[2];
167
+ }
168
+ poseToWorld(skeleton, pose, footIk.world);
169
+ // Legs: each grounded foot back to its sampled place lifted by its ground height.
170
+ for (let i = 0; i < feet.length; i++) {
171
+ if (!footIk.grounded[i])
172
+ continue;
173
+ const c = footIk.chains[i];
174
+ const d = footIk.lift[i];
175
+ target[0] = footIk.tips[i * 3] + upSk[0] * d;
176
+ target[1] = footIk.tips[i * 3 + 1] + upSk[1] * d;
177
+ target[2] = footIk.tips[i * 3 + 2] + upSk[2] * d;
178
+ // The pole: the knee as it is, nudged toward the bend direction so a straight leg knows
179
+ // which way to fold.
180
+ const mo = c.mid * WORLD_NODE_FLOATS;
181
+ const ro = c.root * WORLD_NODE_FLOATS;
182
+ const W = footIk.world;
183
+ const len = Math.hypot(W[mo + 3] - W[ro + 3], W[mo + 7] - W[ro + 7], W[mo + 11] - W[ro + 11]);
184
+ const nudge = Math.max(len, EPS) * 1e-2;
185
+ pole[0] = W[mo + 3] + footIk.bends[i * 3] * nudge;
186
+ pole[1] = W[mo + 7] + footIk.bends[i * 3 + 1] * nudge;
187
+ pole[2] = W[mo + 11] + footIk.bends[i * 3 + 2] * nudge;
188
+ twoBoneIk(pose, skeleton, footIk.world, c, target, pole, null);
189
+ }
190
+ // Feet: each keeps its sampled orientation (the leg's turn is undone at the ankle), then
191
+ // tilts onto its ground normal.
192
+ poseToWorld(skeleton, pose, footIk.world);
193
+ for (let i = 0; i < feet.length; i++) {
194
+ if (!footIk.grounded[i])
195
+ continue;
196
+ const f = feet[i];
197
+ const to = f.tip * WORLD_NODE_FLOATS;
198
+ invertAffine(inv, 0, footIk.world, to);
199
+ multiplyAffine(undo, 0, worldPose, to, inv, 0);
200
+ matrixToAxisAngle(delta, undo);
201
+ const parent = skeleton.parents[f.tip];
202
+ const po = parent < 0 ? 0 : parent * WORLD_NODE_FLOATS;
203
+ const pm = parent < 0 ? IDENTITY : footIk.world;
204
+ applyDelta(pose, f.tip, delta, pm, po, null);
205
+ nrm[0] = footIk.normals[i * 3];
206
+ nrm[1] = footIk.normals[i * 3 + 1];
207
+ nrm[2] = footIk.normals[i * 3 + 2];
208
+ rotationBetween(delta, upN, nrm);
209
+ if (Math.abs(delta[3]) < 1e-5)
210
+ continue;
211
+ applyDelta(pose, f.tip, delta, pm, po, null);
212
+ }
213
+ }
214
+ /** The (unit axis, angle) of the rotation in the upper 3×3 of the 3×4 matrix `m`. */
215
+ function matrixToAxisAngle(out, m) {
216
+ const tr = m[0] + m[5] + m[10];
217
+ out[0] = m[9] - m[6];
218
+ out[1] = m[2] - m[8];
219
+ out[2] = m[4] - m[1];
220
+ const sin2 = Math.hypot(out[0], out[1], out[2]);
221
+ if (sin2 <= EPS) {
222
+ // Identity, or a half turn: take the axis from the symmetric part.
223
+ if (tr > 0) {
224
+ out[0] = 1;
225
+ out[1] = 0;
226
+ out[2] = 0;
227
+ out[3] = 0;
228
+ return;
229
+ }
230
+ out[0] = Math.sqrt(Math.max(0, (m[0] + 1) / 2));
231
+ out[1] = Math.sqrt(Math.max(0, (m[5] + 1) / 2));
232
+ out[2] = Math.sqrt(Math.max(0, (m[10] + 1) / 2));
233
+ normalize(out);
234
+ out[3] = Math.PI;
235
+ return;
236
+ }
237
+ out[0] = out[0] / sin2;
238
+ out[1] = out[1] / sin2;
239
+ out[2] = out[2] / sin2;
240
+ out[3] = Math.atan2(sin2 / 2, (tr - 1) / 2);
241
+ }