@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.
- package/CHANGELOG.md +390 -0
- package/LICENSE +21 -0
- package/README.md +333 -2
- package/dist/analytics/analytics.d.ts +80 -0
- package/dist/analytics/analytics.js +129 -0
- package/dist/analytics/index.d.ts +1 -0
- package/dist/analytics/index.js +1 -0
- package/dist/anim/aim.d.ts +43 -0
- package/dist/anim/aim.js +157 -0
- package/dist/anim/clip.d.ts +98 -0
- package/dist/anim/clip.js +282 -0
- package/dist/anim/compress.d.ts +65 -0
- package/dist/anim/compress.js +339 -0
- package/dist/anim/foot-ik.d.ts +74 -0
- package/dist/anim/foot-ik.js +241 -0
- package/dist/anim/graph.d.ts +153 -0
- package/dist/anim/graph.js +532 -0
- package/dist/anim/ik.d.ts +105 -0
- package/dist/anim/ik.js +435 -0
- package/dist/anim/index.d.ts +36 -0
- package/dist/anim/index.js +21 -0
- package/dist/anim/match.d.ts +66 -0
- package/dist/anim/match.js +153 -0
- package/dist/anim/mixer.d.ts +113 -0
- package/dist/anim/mixer.js +314 -0
- package/dist/anim/morph.d.ts +41 -0
- package/dist/anim/morph.js +94 -0
- package/dist/anim/pose.d.ts +74 -0
- package/dist/anim/pose.js +254 -0
- package/dist/anim/quat.d.ts +44 -0
- package/dist/anim/quat.js +187 -0
- package/dist/anim/retarget.d.ts +97 -0
- package/dist/anim/retarget.js +366 -0
- package/dist/anim/root-motion.d.ts +44 -0
- package/dist/anim/root-motion.js +224 -0
- package/dist/anim/skin-pose.d.ts +39 -0
- package/dist/anim/skin-pose.js +53 -0
- package/dist/anim/socket.d.ts +47 -0
- package/dist/anim/socket.js +84 -0
- package/dist/anim/spring-chain.d.ts +73 -0
- package/dist/anim/spring-chain.js +294 -0
- package/dist/assets/atlas.d.ts +90 -0
- package/dist/assets/atlas.js +212 -0
- package/dist/assets/baked.d.ts +33 -0
- package/dist/assets/baked.js +38 -0
- package/dist/assets/bundle.d.ts +88 -0
- package/dist/assets/bundle.js +464 -0
- package/dist/assets/cache.d.ts +56 -0
- package/dist/assets/cache.js +123 -0
- package/dist/assets/gltf-load.d.ts +252 -0
- package/dist/assets/gltf-load.js +661 -0
- package/dist/assets/gltf-skin.d.ts +36 -0
- package/dist/assets/gltf-skin.js +109 -0
- package/dist/assets/gltf.d.ts +706 -0
- package/dist/assets/gltf.js +1376 -0
- package/dist/assets/index.d.ts +20 -0
- package/dist/assets/index.js +10 -0
- package/dist/assets/ktx2.d.ts +71 -0
- package/dist/assets/ktx2.js +213 -0
- package/dist/assets/manifest.d.ts +79 -0
- package/dist/assets/manifest.js +7 -0
- package/dist/assets/meshopt.d.ts +23 -0
- package/dist/assets/meshopt.js +403 -0
- package/dist/audio/audio.d.ts +733 -0
- package/dist/audio/audio.js +2013 -0
- package/dist/audio/dsp.d.ts +61 -0
- package/dist/audio/dsp.js +159 -0
- package/dist/audio/duck.d.ts +44 -0
- package/dist/audio/duck.js +101 -0
- package/dist/audio/envelope.d.ts +53 -0
- package/dist/audio/envelope.js +106 -0
- package/dist/audio/index.d.ts +13 -0
- package/dist/audio/index.js +14 -0
- package/dist/audio/music.d.ts +113 -0
- package/dist/audio/music.js +548 -0
- package/dist/audio/occlusion.d.ts +55 -0
- package/dist/audio/occlusion.js +61 -0
- package/dist/audio/persist.d.ts +23 -0
- package/dist/audio/persist.js +27 -0
- package/dist/audio/recipes.d.ts +55 -0
- package/dist/audio/recipes.js +227 -0
- package/dist/audio/send.d.ts +41 -0
- package/dist/audio/send.js +118 -0
- package/dist/audio/sprites.d.ts +59 -0
- package/dist/audio/sprites.js +43 -0
- package/dist/audio/synth.d.ts +103 -0
- package/dist/audio/synth.js +197 -0
- package/dist/audio/variations.d.ts +25 -0
- package/dist/audio/variations.js +55 -0
- package/dist/audio/zones.d.ts +100 -0
- package/dist/audio/zones.js +167 -0
- package/dist/core/detached-surface.d.ts +44 -0
- package/dist/core/detached-surface.js +104 -0
- package/dist/core/events.d.ts +24 -0
- package/dist/core/events.js +43 -0
- package/dist/core/game.d.ts +409 -0
- package/dist/core/game.js +530 -0
- package/dist/core/hash.d.ts +24 -0
- package/dist/core/hash.js +49 -0
- package/dist/core/index.d.ts +17 -0
- package/dist/core/index.js +9 -0
- package/dist/core/loop.d.ts +113 -0
- package/dist/core/loop.js +185 -0
- package/dist/core/math.d.ts +58 -0
- package/dist/core/math.js +210 -0
- package/dist/core/noise.d.ts +40 -0
- package/dist/core/noise.js +247 -0
- package/dist/core/pool.d.ts +42 -0
- package/dist/core/pool.js +85 -0
- package/dist/core/random.d.ts +61 -0
- package/dist/core/random.js +199 -0
- package/dist/core/scene.d.ts +79 -0
- package/dist/core/scene.js +195 -0
- package/dist/core/timer.d.ts +66 -0
- package/dist/core/timer.js +286 -0
- package/dist/fx/billboard-wgsl.d.ts +29 -0
- package/dist/fx/billboard-wgsl.js +158 -0
- package/dist/fx/billboard.d.ts +249 -0
- package/dist/fx/billboard.js +702 -0
- package/dist/fx/debug-draw.d.ts +127 -0
- package/dist/fx/debug-draw.js +523 -0
- package/dist/fx/debug-wgsl.d.ts +7 -0
- package/dist/fx/debug-wgsl.js +32 -0
- package/dist/fx/decals-wgsl.d.ts +15 -0
- package/dist/fx/decals-wgsl.js +173 -0
- package/dist/fx/decals.d.ts +216 -0
- package/dist/fx/decals.js +607 -0
- package/dist/fx/effect.d.ts +129 -0
- package/dist/fx/effect.js +349 -0
- package/dist/fx/effects/hit-spark.json +48 -0
- package/dist/fx/effects/index.d.ts +8 -0
- package/dist/fx/effects/index.js +9 -0
- package/dist/fx/effects/magic-aura.json +47 -0
- package/dist/fx/effects/smoke-puff.json +34 -0
- package/dist/fx/emit-surface.d.ts +40 -0
- package/dist/fx/emit-surface.js +124 -0
- package/dist/fx/flicker.d.ts +30 -0
- package/dist/fx/flicker.js +42 -0
- package/dist/fx/index.d.ts +31 -0
- package/dist/fx/index.js +20 -0
- package/dist/fx/particles-wgsl.d.ts +57 -0
- package/dist/fx/particles-wgsl.js +828 -0
- package/dist/fx/particles.d.ts +566 -0
- package/dist/fx/particles.js +1533 -0
- package/dist/fx/random.d.ts +15 -0
- package/dist/fx/random.js +20 -0
- package/dist/fx/sky-wgsl.d.ts +23 -0
- package/dist/fx/sky-wgsl.js +127 -0
- package/dist/fx/sky.d.ts +96 -0
- package/dist/fx/sky.js +175 -0
- package/dist/fx/text-atlas.d.ts +143 -0
- package/dist/fx/text-atlas.js +327 -0
- package/dist/fx/trail-wgsl.d.ts +11 -0
- package/dist/fx/trail-wgsl.js +42 -0
- package/dist/fx/trail.d.ts +194 -0
- package/dist/fx/trail.js +555 -0
- package/dist/fx/water-wgsl.d.ts +34 -0
- package/dist/fx/water-wgsl.js +197 -0
- package/dist/fx/water.d.ts +176 -0
- package/dist/fx/water.js +371 -0
- package/dist/geom/collide/bvh.d.ts +151 -0
- package/dist/geom/collide/bvh.js +493 -0
- package/dist/geom/collide/debug.d.ts +14 -0
- package/dist/geom/collide/debug.js +74 -0
- package/dist/geom/collide/grid.d.ts +26 -0
- package/dist/geom/collide/grid.js +91 -0
- package/dist/geom/collide/index.d.ts +8 -0
- package/dist/geom/collide/index.js +8 -0
- package/dist/geom/collide/queries.d.ts +70 -0
- package/dist/geom/collide/queries.js +583 -0
- package/dist/geom/collide/serialize.d.ts +14 -0
- package/dist/geom/collide/serialize.js +145 -0
- package/dist/geom/collide/shapes.d.ts +161 -0
- package/dist/geom/collide/shapes.js +686 -0
- package/dist/geom/collide/slide.d.ts +66 -0
- package/dist/geom/collide/slide.js +375 -0
- package/dist/geom/collide/tile-slide.d.ts +88 -0
- package/dist/geom/collide/tile-slide.js +255 -0
- package/dist/geom/geometry.d.ts +95 -0
- package/dist/geom/geometry.js +322 -0
- package/dist/geom/heightfield.d.ts +72 -0
- package/dist/geom/heightfield.js +295 -0
- package/dist/geom/hull.d.ts +27 -0
- package/dist/geom/hull.js +279 -0
- package/dist/geom/index.d.ts +35 -0
- package/dist/geom/index.js +23 -0
- package/dist/geom/iso.d.ts +74 -0
- package/dist/geom/iso.js +89 -0
- package/dist/geom/lod.d.ts +29 -0
- package/dist/geom/lod.js +27 -0
- package/dist/geom/ops.d.ts +47 -0
- package/dist/geom/ops.js +285 -0
- package/dist/geom/primitives.d.ts +97 -0
- package/dist/geom/primitives.js +359 -0
- package/dist/geom/shapes.d.ts +43 -0
- package/dist/geom/shapes.js +88 -0
- package/dist/geom/simplify.d.ts +38 -0
- package/dist/geom/simplify.js +519 -0
- package/dist/geom/split.d.ts +37 -0
- package/dist/geom/split.js +413 -0
- package/dist/geom/sweep.d.ts +83 -0
- package/dist/geom/sweep.js +534 -0
- package/dist/geom/tangents.d.ts +16 -0
- package/dist/geom/tangents.js +124 -0
- package/dist/geom/triangulate.d.ts +19 -0
- package/dist/geom/triangulate.js +147 -0
- package/dist/host/default.d.ts +10 -0
- package/dist/host/default.js +10 -0
- package/dist/host/index.d.ts +4 -0
- package/dist/host/index.js +3 -0
- package/dist/host/types.d.ts +144 -0
- package/dist/host/types.js +1 -0
- package/dist/host/web-links.d.ts +38 -0
- package/dist/host/web-links.js +110 -0
- package/dist/host/web.d.ts +30 -0
- package/dist/host/web.js +216 -0
- package/dist/host/xr.d.ts +24 -0
- package/dist/host/xr.js +59 -0
- package/dist/i18n/i18n.d.ts +59 -0
- package/dist/i18n/i18n.js +241 -0
- package/dist/i18n/index.d.ts +3 -0
- package/dist/i18n/index.js +3 -0
- package/dist/i18n/message.d.ts +72 -0
- package/dist/i18n/message.js +369 -0
- package/dist/i18n/negotiate.d.ts +17 -0
- package/dist/i18n/negotiate.js +58 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +1 -0
- package/dist/input/bindings.d.ts +31 -0
- package/dist/input/bindings.js +141 -0
- package/dist/input/clock.d.ts +44 -0
- package/dist/input/clock.js +49 -0
- package/dist/input/follow.d.ts +133 -0
- package/dist/input/follow.js +218 -0
- package/dist/input/gestures.d.ts +281 -0
- package/dist/input/gestures.js +662 -0
- package/dist/input/glyphs.d.ts +35 -0
- package/dist/input/glyphs.js +216 -0
- package/dist/input/gyro.d.ts +85 -0
- package/dist/input/gyro.js +140 -0
- package/dist/input/index.d.ts +19 -0
- package/dist/input/index.js +16 -0
- package/dist/input/input.d.ts +543 -0
- package/dist/input/input.js +1492 -0
- package/dist/input/look.d.ts +327 -0
- package/dist/input/look.js +681 -0
- package/dist/input/magnify.d.ts +177 -0
- package/dist/input/magnify.js +131 -0
- package/dist/input/pointer-dispatch.d.ts +138 -0
- package/dist/input/pointer-dispatch.js +180 -0
- package/dist/input/pointer-router.d.ts +204 -0
- package/dist/input/pointer-router.js +533 -0
- package/dist/input/pointer-sources.d.ts +164 -0
- package/dist/input/pointer-sources.js +306 -0
- package/dist/input/rays.d.ts +138 -0
- package/dist/input/rays.js +615 -0
- package/dist/input/record.d.ts +209 -0
- package/dist/input/record.js +601 -0
- package/dist/input/touch-zones.d.ts +147 -0
- package/dist/input/touch-zones.js +300 -0
- package/dist/input/types.d.ts +158 -0
- package/dist/input/types.js +1 -0
- package/dist/input/xr-source.d.ts +53 -0
- package/dist/input/xr-source.js +164 -0
- package/dist/nav/astar.d.ts +45 -0
- package/dist/nav/astar.js +182 -0
- package/dist/nav/corridor.d.ts +40 -0
- package/dist/nav/corridor.js +95 -0
- package/dist/nav/crowd.d.ts +94 -0
- package/dist/nav/crowd.js +624 -0
- package/dist/nav/flow.d.ts +61 -0
- package/dist/nav/flow.js +193 -0
- package/dist/nav/grid.d.ts +108 -0
- package/dist/nav/grid.js +210 -0
- package/dist/nav/heap.d.ts +22 -0
- package/dist/nav/heap.js +92 -0
- package/dist/nav/index.d.ts +37 -0
- package/dist/nav/index.js +19 -0
- package/dist/nav/mesh/areas.d.ts +44 -0
- package/dist/nav/mesh/areas.js +98 -0
- package/dist/nav/mesh/bake-options.d.ts +33 -0
- package/dist/nav/mesh/bake-options.js +76 -0
- package/dist/nav/mesh/contour.d.ts +22 -0
- package/dist/nav/mesh/contour.js +323 -0
- package/dist/nav/mesh/flow3.d.ts +35 -0
- package/dist/nav/mesh/flow3.js +229 -0
- package/dist/nav/mesh/links.d.ts +38 -0
- package/dist/nav/mesh/links.js +72 -0
- package/dist/nav/mesh/mesh-path.d.ts +152 -0
- package/dist/nav/mesh/mesh-path.js +643 -0
- package/dist/nav/mesh/mesh-reach.d.ts +78 -0
- package/dist/nav/mesh/mesh-reach.js +450 -0
- package/dist/nav/mesh/navmesh.d.ts +117 -0
- package/dist/nav/mesh/navmesh.js +647 -0
- package/dist/nav/mesh/polymesh.d.ts +24 -0
- package/dist/nav/mesh/polymesh.js +384 -0
- package/dist/nav/mesh/regions.d.ts +12 -0
- package/dist/nav/mesh/regions.js +367 -0
- package/dist/nav/mesh/serialize.d.ts +13 -0
- package/dist/nav/mesh/serialize.js +197 -0
- package/dist/nav/mesh/voxelize.d.ts +111 -0
- package/dist/nav/mesh/voxelize.js +552 -0
- package/dist/nav/obstacles.d.ts +75 -0
- package/dist/nav/obstacles.js +294 -0
- package/dist/nav/path.d.ts +59 -0
- package/dist/nav/path.js +177 -0
- package/dist/nav/query-queue.d.ts +46 -0
- package/dist/nav/query-queue.js +308 -0
- package/dist/nav/reach.d.ts +53 -0
- package/dist/nav/reach.js +148 -0
- package/dist/nav/steer.d.ts +36 -0
- package/dist/nav/steer.js +78 -0
- package/dist/platform/boot.d.ts +28 -0
- package/dist/platform/boot.js +43 -0
- package/dist/platform/device-mode.d.ts +40 -0
- package/dist/platform/device-mode.js +104 -0
- package/dist/platform/device.d.ts +31 -0
- package/dist/platform/device.js +91 -0
- package/dist/platform/environment.d.ts +42 -0
- package/dist/platform/environment.js +82 -0
- package/dist/platform/gpu.d.ts +40 -0
- package/dist/platform/gpu.js +57 -0
- package/dist/platform/index.d.ts +18 -0
- package/dist/platform/index.js +10 -0
- package/dist/platform/letterbox.d.ts +43 -0
- package/dist/platform/letterbox.js +57 -0
- package/dist/platform/links.d.ts +20 -0
- package/dist/platform/links.js +43 -0
- package/dist/platform/screens.d.ts +36 -0
- package/dist/platform/screens.js +138 -0
- package/dist/platform/surface.d.ts +72 -0
- package/dist/platform/surface.js +157 -0
- package/dist/platform/xr-fake.d.ts +168 -0
- package/dist/platform/xr-fake.js +434 -0
- package/dist/platform/xr-surface.d.ts +120 -0
- package/dist/platform/xr-surface.js +214 -0
- package/dist/platform/xr-types.d.ts +262 -0
- package/dist/post/ao.d.ts +12 -0
- package/dist/post/ao.js +136 -0
- package/dist/post/blend.d.ts +27 -0
- package/dist/post/blend.js +129 -0
- package/dist/post/depth-pass.d.ts +43 -0
- package/dist/post/depth-pass.js +118 -0
- package/dist/post/dof.d.ts +1 -0
- package/dist/post/dof.js +66 -0
- package/dist/post/exposure.d.ts +80 -0
- package/dist/post/exposure.js +322 -0
- package/dist/post/index.d.ts +12 -0
- package/dist/post/index.js +11 -0
- package/dist/post/lens-dirt.d.ts +13 -0
- package/dist/post/lens-dirt.js +91 -0
- package/dist/post/lut.d.ts +19 -0
- package/dist/post/lut.js +50 -0
- package/dist/post/motion-blur.d.ts +9 -0
- package/dist/post/motion-blur.js +78 -0
- package/dist/post/post-stack.d.ts +302 -0
- package/dist/post/post-stack.js +1137 -0
- package/dist/post/settings.d.ts +485 -0
- package/dist/post/settings.js +416 -0
- package/dist/post/shaders.d.ts +25 -0
- package/dist/post/shaders.js +410 -0
- package/dist/post/ssr.d.ts +16 -0
- package/dist/post/ssr.js +155 -0
- package/dist/post/taa.d.ts +74 -0
- package/dist/post/taa.js +235 -0
- package/dist/post/white-balance.d.ts +25 -0
- package/dist/post/white-balance.js +98 -0
- package/dist/render/bindings.d.ts +98 -0
- package/dist/render/bindings.js +281 -0
- package/dist/render/blend.d.ts +13 -0
- package/dist/render/blend.js +26 -0
- package/dist/render/camera-2d.d.ts +72 -0
- package/dist/render/camera-2d.js +92 -0
- package/dist/render/camera.d.ts +302 -0
- package/dist/render/camera.js +671 -0
- package/dist/render/color.d.ts +16 -0
- package/dist/render/color.js +41 -0
- package/dist/render/compressed.d.ts +104 -0
- package/dist/render/compressed.js +248 -0
- package/dist/render/diagnostics.d.ts +15 -0
- package/dist/render/diagnostics.js +43 -0
- package/dist/render/dynamic-resolution.d.ts +70 -0
- package/dist/render/dynamic-resolution.js +126 -0
- package/dist/render/fake-gpu.d.ts +204 -0
- package/dist/render/fake-gpu.js +426 -0
- package/dist/render/flipbook.d.ts +56 -0
- package/dist/render/flipbook.js +87 -0
- package/dist/render/frame.d.ts +278 -0
- package/dist/render/frame.js +174 -0
- package/dist/render/fullscreen.d.ts +54 -0
- package/dist/render/fullscreen.js +91 -0
- package/dist/render/gpu-timer.d.ts +73 -0
- package/dist/render/gpu-timer.js +186 -0
- package/dist/render/index.d.ts +53 -0
- package/dist/render/index.js +29 -0
- package/dist/render/instance-list.d.ts +185 -0
- package/dist/render/instance-list.js +547 -0
- package/dist/render/lights.d.ts +181 -0
- package/dist/render/lights.js +233 -0
- package/dist/render/lod.d.ts +54 -0
- package/dist/render/lod.js +52 -0
- package/dist/render/material.d.ts +165 -0
- package/dist/render/material.js +292 -0
- package/dist/render/mesh.d.ts +284 -0
- package/dist/render/mesh.js +472 -0
- package/dist/render/mipmap.d.ts +16 -0
- package/dist/render/mipmap.js +121 -0
- package/dist/render/parallax.d.ts +50 -0
- package/dist/render/parallax.js +47 -0
- package/dist/render/pbr-wgsl.d.ts +31 -0
- package/dist/render/pbr-wgsl.js +119 -0
- package/dist/render/ping-pong.d.ts +42 -0
- package/dist/render/ping-pong.js +74 -0
- package/dist/render/renderer.d.ts +741 -0
- package/dist/render/renderer.js +2486 -0
- package/dist/render/scene-pipelines.d.ts +44 -0
- package/dist/render/scene-pipelines.js +92 -0
- package/dist/render/scene-target.d.ts +170 -0
- package/dist/render/scene-target.js +450 -0
- package/dist/render/shadow.d.ts +74 -0
- package/dist/render/shadow.js +208 -0
- package/dist/render/skin.d.ts +112 -0
- package/dist/render/skin.js +240 -0
- package/dist/render/snapshot.d.ts +36 -0
- package/dist/render/snapshot.js +34 -0
- package/dist/render/standard.d.ts +162 -0
- package/dist/render/standard.js +263 -0
- package/dist/render/texture.d.ts +142 -0
- package/dist/render/texture.js +228 -0
- package/dist/render/tile-layer.d.ts +110 -0
- package/dist/render/tile-layer.js +304 -0
- package/dist/render/transform.d.ts +115 -0
- package/dist/render/transform.js +318 -0
- package/dist/render/wgsl.d.ts +131 -0
- package/dist/render/wgsl.js +528 -0
- package/dist/save/codec.d.ts +49 -0
- package/dist/save/codec.js +169 -0
- package/dist/save/index.d.ts +8 -0
- package/dist/save/index.js +8 -0
- package/dist/save/journal.d.ts +24 -0
- package/dist/save/journal.js +102 -0
- package/dist/save/migrate.d.ts +15 -0
- package/dist/save/migrate.js +37 -0
- package/dist/save/schema.d.ts +72 -0
- package/dist/save/schema.js +154 -0
- package/dist/save/settings.d.ts +28 -0
- package/dist/save/settings.js +59 -0
- package/dist/save/slots.d.ts +56 -0
- package/dist/save/slots.js +200 -0
- package/dist/save/storage.d.ts +35 -0
- package/dist/save/storage.js +62 -0
- package/dist/save/store.d.ts +114 -0
- package/dist/save/store.js +269 -0
- package/dist/testing/fake-host.d.ts +103 -0
- package/dist/testing/fake-host.js +163 -0
- package/dist/testing/fixed-clock.d.ts +25 -0
- package/dist/testing/fixed-clock.js +30 -0
- package/dist/testing/index.d.ts +4 -0
- package/dist/testing/index.js +5 -0
- package/dist/testing/test-game.d.ts +76 -0
- package/dist/testing/test-game.js +92 -0
- package/dist/tween/ease.d.ts +77 -0
- package/dist/tween/ease.js +192 -0
- package/dist/tween/index.d.ts +8 -0
- package/dist/tween/index.js +7 -0
- package/dist/tween/timeline.d.ts +78 -0
- package/dist/tween/timeline.js +269 -0
- package/dist/tween/tween.d.ts +168 -0
- package/dist/tween/tween.js +320 -0
- package/dist/ui/anchor.d.ts +206 -0
- package/dist/ui/anchor.js +307 -0
- package/dist/ui/caption.d.ts +122 -0
- package/dist/ui/caption.js +286 -0
- package/dist/ui/cursor.d.ts +105 -0
- package/dist/ui/cursor.js +261 -0
- package/dist/ui/dom.d.ts +30 -0
- package/dist/ui/dom.js +92 -0
- package/dist/ui/floats.d.ts +86 -0
- package/dist/ui/floats.js +260 -0
- package/dist/ui/hud.d.ts +198 -0
- package/dist/ui/hud.js +431 -0
- package/dist/ui/index.d.ts +37 -0
- package/dist/ui/index.js +34 -0
- package/dist/ui/inventory-card.d.ts +52 -0
- package/dist/ui/inventory-card.js +173 -0
- package/dist/ui/inventory-context.d.ts +29 -0
- package/dist/ui/inventory-context.js +125 -0
- package/dist/ui/inventory-drag.d.ts +63 -0
- package/dist/ui/inventory-drag.js +394 -0
- package/dist/ui/inventory-grid.d.ts +66 -0
- package/dist/ui/inventory-grid.js +625 -0
- package/dist/ui/inventory-radial.d.ts +39 -0
- package/dist/ui/inventory-radial.js +168 -0
- package/dist/ui/inventory.d.ts +128 -0
- package/dist/ui/inventory.js +154 -0
- package/dist/ui/layout.d.ts +356 -0
- package/dist/ui/layout.js +856 -0
- package/dist/ui/link.d.ts +64 -0
- package/dist/ui/link.js +155 -0
- package/dist/ui/loading.d.ts +40 -0
- package/dist/ui/loading.js +157 -0
- package/dist/ui/loupe.d.ts +74 -0
- package/dist/ui/loupe.js +164 -0
- package/dist/ui/menu-nav.d.ts +33 -0
- package/dist/ui/menu-nav.js +28 -0
- package/dist/ui/minimap-canvas.d.ts +145 -0
- package/dist/ui/minimap-canvas.js +460 -0
- package/dist/ui/notify.d.ts +40 -0
- package/dist/ui/notify.js +141 -0
- package/dist/ui/orb.d.ts +53 -0
- package/dist/ui/orb.js +304 -0
- package/dist/ui/overlay.d.ts +213 -0
- package/dist/ui/overlay.js +394 -0
- package/dist/ui/padnav.d.ts +53 -0
- package/dist/ui/padnav.js +146 -0
- package/dist/ui/perf.d.ts +130 -0
- package/dist/ui/perf.js +329 -0
- package/dist/ui/rotate-card.d.ts +54 -0
- package/dist/ui/rotate-card.js +97 -0
- package/dist/ui/stack.d.ts +128 -0
- package/dist/ui/stack.js +312 -0
- package/dist/ui/styles.d.ts +13 -0
- package/dist/ui/styles.js +200 -0
- package/dist/ui/text-scale.d.ts +67 -0
- package/dist/ui/text-scale.js +102 -0
- package/dist/ui/theme.d.ts +70 -0
- package/dist/ui/theme.js +98 -0
- package/dist/ui/toast.d.ts +23 -0
- package/dist/ui/toast.js +46 -0
- package/dist/ui/tooltip.d.ts +89 -0
- package/dist/ui/tooltip.js +333 -0
- package/dist/ui/widgets-2.d.ts +134 -0
- package/dist/ui/widgets-2.js +399 -0
- package/dist/ui/widgets-3.d.ts +91 -0
- package/dist/ui/widgets-3.js +212 -0
- package/dist/ui/widgets.d.ts +34 -0
- package/dist/ui/widgets.js +50 -0
- package/dist/ui/world-panel/index.d.ts +5 -0
- package/dist/ui/world-panel/index.js +5 -0
- package/dist/ui/world-panel/layout.d.ts +38 -0
- package/dist/ui/world-panel/layout.js +68 -0
- package/dist/ui/world-panel/paint.d.ts +96 -0
- package/dist/ui/world-panel/paint.js +136 -0
- package/dist/ui/world-panel/panel.d.ts +235 -0
- package/dist/ui/world-panel/panel.js +762 -0
- package/dist/ui/world-panel/wgsl.d.ts +10 -0
- package/dist/ui/world-panel/wgsl.js +52 -0
- package/dist/ui/world-panel/widgets.d.ts +207 -0
- package/dist/ui/world-panel/widgets.js +569 -0
- package/dist/voxel/chunks.d.ts +77 -0
- package/dist/voxel/chunks.js +149 -0
- package/dist/voxel/grid.d.ts +84 -0
- package/dist/voxel/grid.js +223 -0
- package/dist/voxel/index.d.ts +22 -0
- package/dist/voxel/index.js +7 -0
- package/dist/voxel/mesher-worker.d.ts +14 -0
- package/dist/voxel/mesher-worker.js +47 -0
- package/dist/voxel/mesher.d.ts +73 -0
- package/dist/voxel/mesher.js +306 -0
- package/dist/voxel/ray.d.ts +32 -0
- package/dist/voxel/ray.js +73 -0
- package/dist/voxel/wgsl.d.ts +16 -0
- package/dist/voxel/wgsl.js +28 -0
- package/dist/voxel/worker-client.d.ts +79 -0
- package/dist/voxel/worker-client.js +122 -0
- package/package.json +177 -3
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secondary motion: a chain of joints (tail, cape, hair, antenna) simulated as Verlet particles
|
|
3
|
+
* in instance space that spring back toward the sampled pose, with gravity, damping, bone-length
|
|
4
|
+
* constraints, an angle limit and sphere/capsule colliders. After the step the joints' local
|
|
5
|
+
* rotations are rewritten so each bone points at its simulated child; translations are kept.
|
|
6
|
+
* The rest position each frame is the sampled pose, so the animation still drives the chain
|
|
7
|
+
* and the simulation adds lag and sway. Same conventions as `ik.ts`: `worldPose` is
|
|
8
|
+
* `poseToWorld` of the sampled `pose`, `instanceWorld` is the 3×4 instance matrix (`null` for
|
|
9
|
+
* the identity). Allocates in `createSpringChain`, nothing per frame.
|
|
10
|
+
*/
|
|
11
|
+
import { anyPerpendicular, applyDelta, IDENTITY, normalize, rotateBy, rotateByQuat, rotationBetween, transformDir, transformPoint, } from './ik.js';
|
|
12
|
+
import { invertAffine, WORLD_NODE_FLOATS } from './pose.js';
|
|
13
|
+
import { quatFromAxisAngle, quatMultiply, quatNormalize } from './quat.js';
|
|
14
|
+
const EPS = 1e-6;
|
|
15
|
+
/** Validate and allocate; call once. */
|
|
16
|
+
export function createSpringChain(skeleton, desc) {
|
|
17
|
+
const n = skeleton.parents.length;
|
|
18
|
+
let joints;
|
|
19
|
+
if (desc.joints) {
|
|
20
|
+
joints = Int16Array.from(desc.joints);
|
|
21
|
+
if (joints.length < 2)
|
|
22
|
+
throw new Error('SpringChain: at least two joints');
|
|
23
|
+
if (joints[0] !== desc.root)
|
|
24
|
+
throw new Error('SpringChain: joints must start at root');
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
if (desc.tip === undefined)
|
|
28
|
+
throw new Error('SpringChain: tip or joints');
|
|
29
|
+
let count = 0;
|
|
30
|
+
for (let j = desc.tip; j >= 0 && j !== desc.root; j = skeleton.parents[j])
|
|
31
|
+
count++;
|
|
32
|
+
joints = new Int16Array(count + 1);
|
|
33
|
+
let j = desc.tip;
|
|
34
|
+
for (let i = count; i >= 1; i--) {
|
|
35
|
+
joints[i] = j;
|
|
36
|
+
j = skeleton.parents[j];
|
|
37
|
+
}
|
|
38
|
+
joints[0] = j;
|
|
39
|
+
if (j !== desc.root)
|
|
40
|
+
throw new Error(`SpringChain: tip ${desc.tip} does not descend from root`);
|
|
41
|
+
}
|
|
42
|
+
for (let i = 0; i < joints.length; i++) {
|
|
43
|
+
const j = joints[i];
|
|
44
|
+
if (j < 0 || j >= n)
|
|
45
|
+
throw new Error(`SpringChain: joint ${j} must be in [0, ${n})`);
|
|
46
|
+
if (i > 0) {
|
|
47
|
+
let p = skeleton.parents[j];
|
|
48
|
+
while (p >= 0 && p !== joints[i - 1])
|
|
49
|
+
p = skeleton.parents[p];
|
|
50
|
+
if (p < 0)
|
|
51
|
+
throw new Error(`SpringChain: joint ${j} does not descend from ${joints[i - 1]}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const count = joints.length;
|
|
55
|
+
return {
|
|
56
|
+
joints,
|
|
57
|
+
stiffness: desc.stiffness ?? 80,
|
|
58
|
+
damping: desc.damping ?? 6,
|
|
59
|
+
gravity: Float32Array.from(desc.gravity ?? [0, -9.8, 0]),
|
|
60
|
+
drag: desc.drag ?? 0,
|
|
61
|
+
radius: desc.radius ?? 0,
|
|
62
|
+
limitAngle: desc.limitAngle ?? Math.PI,
|
|
63
|
+
collide: desc.collide ?? [],
|
|
64
|
+
maxDt: desc.maxDt ?? 1 / 120,
|
|
65
|
+
teleportDistance: desc.teleportDistance ?? 1,
|
|
66
|
+
positions: new Float32Array(count * 3),
|
|
67
|
+
animated: new Float32Array(count * 3),
|
|
68
|
+
prev: new Float32Array(count * 3),
|
|
69
|
+
animPrev: new Float32Array(count * 3),
|
|
70
|
+
lengths: new Float32Array(count),
|
|
71
|
+
started: false,
|
|
72
|
+
reset() {
|
|
73
|
+
this.started = false;
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
const inv = new Float32Array(WORLD_NODE_FLOATS);
|
|
78
|
+
const p = new Float32Array(3);
|
|
79
|
+
const a = new Float32Array(3);
|
|
80
|
+
const b = new Float32Array(3);
|
|
81
|
+
const delta = new Float32Array(4);
|
|
82
|
+
const acc = new Float32Array(4);
|
|
83
|
+
const dq = new Float32Array(4);
|
|
84
|
+
/**
|
|
85
|
+
* Step the chain by `dt` (split into sub-steps of at most `maxDt`) and rewrite the chain's
|
|
86
|
+
* rotations in `pose`. Call after sampling and `poseToWorld`, before skinning. No allocation.
|
|
87
|
+
*/
|
|
88
|
+
export function updateSpringChain(pose, skeleton, worldPose, chain, instanceWorld, dt) {
|
|
89
|
+
const joints = chain.joints;
|
|
90
|
+
const count = joints.length;
|
|
91
|
+
const pos = chain.positions;
|
|
92
|
+
const prev = chain.prev;
|
|
93
|
+
const anim = chain.animated;
|
|
94
|
+
const animPrev = chain.animPrev;
|
|
95
|
+
// Sampled positions into instance space; bone lengths from them.
|
|
96
|
+
animPrev.set(anim);
|
|
97
|
+
for (let i = 0; i < count; i++) {
|
|
98
|
+
const wo = joints[i] * WORLD_NODE_FLOATS;
|
|
99
|
+
p[0] = worldPose[wo + 3];
|
|
100
|
+
p[1] = worldPose[wo + 7];
|
|
101
|
+
p[2] = worldPose[wo + 11];
|
|
102
|
+
if (instanceWorld)
|
|
103
|
+
transformPoint(a, instanceWorld, 0, p);
|
|
104
|
+
else
|
|
105
|
+
a.set(p);
|
|
106
|
+
anim[i * 3] = a[0];
|
|
107
|
+
anim[i * 3 + 1] = a[1];
|
|
108
|
+
anim[i * 3 + 2] = a[2];
|
|
109
|
+
if (i > 0) {
|
|
110
|
+
chain.lengths[i] = Math.hypot(a[0] - anim[i * 3 - 3], a[1] - anim[i * 3 - 2], a[2] - anim[i * 3 - 1]);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (!chain.started) {
|
|
114
|
+
pos.set(anim);
|
|
115
|
+
prev.set(anim);
|
|
116
|
+
animPrev.set(anim);
|
|
117
|
+
chain.started = true;
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
// A teleport moves the whole chain with the root, keeping its velocity.
|
|
121
|
+
const sx = anim[0] - animPrev[0];
|
|
122
|
+
const sy = anim[1] - animPrev[1];
|
|
123
|
+
const sz = anim[2] - animPrev[2];
|
|
124
|
+
if (Math.hypot(sx, sy, sz) > chain.teleportDistance) {
|
|
125
|
+
for (let i = 0; i < count; i++) {
|
|
126
|
+
pos[i * 3] = pos[i * 3] + sx;
|
|
127
|
+
pos[i * 3 + 1] = pos[i * 3 + 1] + sy;
|
|
128
|
+
pos[i * 3 + 2] = pos[i * 3 + 2] + sz;
|
|
129
|
+
prev[i * 3] = prev[i * 3] + sx;
|
|
130
|
+
prev[i * 3 + 1] = prev[i * 3 + 1] + sy;
|
|
131
|
+
prev[i * 3 + 2] = prev[i * 3 + 2] + sz;
|
|
132
|
+
animPrev[i * 3] = animPrev[i * 3] + sx;
|
|
133
|
+
animPrev[i * 3 + 1] = animPrev[i * 3 + 1] + sy;
|
|
134
|
+
animPrev[i * 3 + 2] = animPrev[i * 3 + 2] + sz;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
// Verlet sub-steps.
|
|
139
|
+
const steps = dt > 0 ? Math.max(1, Math.ceil(dt / Math.max(chain.maxDt, 1e-4))) : 0;
|
|
140
|
+
const h = steps > 0 ? dt / steps : 0;
|
|
141
|
+
const decay = Math.max(0, 1 - chain.damping * h);
|
|
142
|
+
const drag = Math.min(1, Math.max(0, chain.drag)) / Math.max(1, steps);
|
|
143
|
+
const k = chain.stiffness * h * h;
|
|
144
|
+
const gx = chain.gravity[0] * h * h;
|
|
145
|
+
const gy = chain.gravity[1] * h * h;
|
|
146
|
+
const gz = chain.gravity[2] * h * h;
|
|
147
|
+
const limit = chain.limitAngle;
|
|
148
|
+
for (let s = 0; s < steps; s++) {
|
|
149
|
+
// Integrate every joint but the pinned root.
|
|
150
|
+
for (let i = 1; i < count; i++) {
|
|
151
|
+
const o = i * 3;
|
|
152
|
+
const x = pos[o];
|
|
153
|
+
const y = pos[o + 1];
|
|
154
|
+
const z = pos[o + 2];
|
|
155
|
+
const vx = (x - prev[o]) * decay + (anim[o] - animPrev[o]) * drag;
|
|
156
|
+
const vy = (y - prev[o + 1]) * decay + (anim[o + 1] - animPrev[o + 1]) * drag;
|
|
157
|
+
const vz = (z - prev[o + 2]) * decay + (anim[o + 2] - animPrev[o + 2]) * drag;
|
|
158
|
+
prev[o] = x;
|
|
159
|
+
prev[o + 1] = y;
|
|
160
|
+
prev[o + 2] = z;
|
|
161
|
+
pos[o] = x + vx + (anim[o] - x) * k + gx;
|
|
162
|
+
pos[o + 1] = y + vy + (anim[o + 1] - y) * k + gy;
|
|
163
|
+
pos[o + 2] = z + vz + (anim[o + 2] - z) * k + gz;
|
|
164
|
+
}
|
|
165
|
+
pos[0] = anim[0];
|
|
166
|
+
pos[1] = anim[1];
|
|
167
|
+
pos[2] = anim[2];
|
|
168
|
+
// Constraints, parent before child: bone length, angle limit, colliders.
|
|
169
|
+
for (let i = 1; i < count; i++) {
|
|
170
|
+
const o = i * 3;
|
|
171
|
+
const po = o - 3;
|
|
172
|
+
const len = chain.lengths[i];
|
|
173
|
+
a[0] = pos[o] - pos[po];
|
|
174
|
+
a[1] = pos[o + 1] - pos[po + 1];
|
|
175
|
+
a[2] = pos[o + 2] - pos[po + 2];
|
|
176
|
+
if (normalize(a) <= EPS) {
|
|
177
|
+
b[0] = anim[o] - anim[po];
|
|
178
|
+
b[1] = anim[o + 1] - anim[po + 1];
|
|
179
|
+
b[2] = anim[o + 2] - anim[po + 2];
|
|
180
|
+
if (normalize(b) <= EPS)
|
|
181
|
+
anyPerpendicular(b, UP);
|
|
182
|
+
a.set(b);
|
|
183
|
+
}
|
|
184
|
+
if (limit < Math.PI) {
|
|
185
|
+
b[0] = anim[o] - anim[po];
|
|
186
|
+
b[1] = anim[o + 1] - anim[po + 1];
|
|
187
|
+
b[2] = anim[o + 2] - anim[po + 2];
|
|
188
|
+
if (normalize(b) > EPS) {
|
|
189
|
+
rotationBetween(delta, b, a);
|
|
190
|
+
if (delta[3] > limit) {
|
|
191
|
+
a.set(b);
|
|
192
|
+
rotateBy(a, delta, limit);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
pos[o] = pos[po] + a[0] * len;
|
|
197
|
+
pos[o + 1] = pos[po + 1] + a[1] * len;
|
|
198
|
+
pos[o + 2] = pos[po + 2] + a[2] * len;
|
|
199
|
+
const colliders = chain.collide;
|
|
200
|
+
for (let c = 0; c < colliders.length; c++)
|
|
201
|
+
pushOut(pos, o, colliders[c], chain.radius);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
// Rotations: each joint turns so its bone points at the simulated child. Deltas accumulate
|
|
205
|
+
// down the chain (`acc`, instance space); each one's axis is taken back through the
|
|
206
|
+
// accumulated turn and the instance matrix before `applyDelta` sees the parent's old matrix.
|
|
207
|
+
if (instanceWorld)
|
|
208
|
+
invertAffine(inv, 0, instanceWorld, 0);
|
|
209
|
+
acc[0] = 0;
|
|
210
|
+
acc[1] = 0;
|
|
211
|
+
acc[2] = 0;
|
|
212
|
+
acc[3] = 1;
|
|
213
|
+
for (let i = 0; i + 1 < count; i++) {
|
|
214
|
+
const o = i * 3;
|
|
215
|
+
a[0] = anim[o + 3] - anim[o];
|
|
216
|
+
a[1] = anim[o + 4] - anim[o + 1];
|
|
217
|
+
a[2] = anim[o + 5] - anim[o + 2];
|
|
218
|
+
if (normalize(a) <= EPS)
|
|
219
|
+
continue;
|
|
220
|
+
rotateByQuat(a, acc, 0);
|
|
221
|
+
b[0] = pos[o + 3] - pos[o];
|
|
222
|
+
b[1] = pos[o + 4] - pos[o + 1];
|
|
223
|
+
b[2] = pos[o + 5] - pos[o + 2];
|
|
224
|
+
if (normalize(b) <= EPS)
|
|
225
|
+
continue;
|
|
226
|
+
rotationBetween(delta, a, b);
|
|
227
|
+
if (delta[3] < 1e-7)
|
|
228
|
+
continue;
|
|
229
|
+
quatFromAxisAngle(dq, 0, delta[0], delta[1], delta[2], delta[3]);
|
|
230
|
+
// Axis into the frame the parent's sampled matrix lives in.
|
|
231
|
+
acc[0] = -acc[0];
|
|
232
|
+
acc[1] = -acc[1];
|
|
233
|
+
acc[2] = -acc[2];
|
|
234
|
+
rotateByQuat(delta, acc, 0);
|
|
235
|
+
acc[0] = -acc[0];
|
|
236
|
+
acc[1] = -acc[1];
|
|
237
|
+
acc[2] = -acc[2];
|
|
238
|
+
if (instanceWorld) {
|
|
239
|
+
transformDir(delta, inv, 0, delta);
|
|
240
|
+
normalize(delta);
|
|
241
|
+
}
|
|
242
|
+
const j = joints[i];
|
|
243
|
+
const parent = skeleton.parents[j];
|
|
244
|
+
if (parent < 0)
|
|
245
|
+
applyDelta(pose, j, delta, IDENTITY, 0, null);
|
|
246
|
+
else
|
|
247
|
+
applyDelta(pose, j, delta, worldPose, parent * WORLD_NODE_FLOATS, null);
|
|
248
|
+
quatMultiply(acc, 0, dq, 0, acc, 0);
|
|
249
|
+
quatNormalize(acc, 0);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
const UP = new Float32Array([0, 1, 0]);
|
|
253
|
+
const cp = new Float32Array(3);
|
|
254
|
+
/** Move `pos[o..]` out of `c`, keeping `radius` clear of its surface. */
|
|
255
|
+
function pushOut(pos, o, c, radius) {
|
|
256
|
+
const x = pos[o];
|
|
257
|
+
const y = pos[o + 1];
|
|
258
|
+
const z = pos[o + 2];
|
|
259
|
+
if ('a' in c) {
|
|
260
|
+
// Closest point on the segment.
|
|
261
|
+
const ax = c.a[0];
|
|
262
|
+
const ay = c.a[1];
|
|
263
|
+
const az = c.a[2];
|
|
264
|
+
const dx = c.b[0] - ax;
|
|
265
|
+
const dy = c.b[1] - ay;
|
|
266
|
+
const dz = c.b[2] - az;
|
|
267
|
+
const dd = dx * dx + dy * dy + dz * dz;
|
|
268
|
+
let t = dd > EPS ? ((x - ax) * dx + (y - ay) * dy + (z - az) * dz) / dd : 0;
|
|
269
|
+
t = Math.min(1, Math.max(0, t));
|
|
270
|
+
cp[0] = ax + dx * t;
|
|
271
|
+
cp[1] = ay + dy * t;
|
|
272
|
+
cp[2] = az + dz * t;
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
cp[0] = c.center[0];
|
|
276
|
+
cp[1] = c.center[1];
|
|
277
|
+
cp[2] = c.center[2];
|
|
278
|
+
}
|
|
279
|
+
const nx = x - cp[0];
|
|
280
|
+
const ny = y - cp[1];
|
|
281
|
+
const nz = z - cp[2];
|
|
282
|
+
const dist = Math.hypot(nx, ny, nz);
|
|
283
|
+
const want = c.radius + radius;
|
|
284
|
+
if (dist >= want)
|
|
285
|
+
return;
|
|
286
|
+
if (dist <= EPS) {
|
|
287
|
+
pos[o + 1] = y + want;
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
const s = want / dist;
|
|
291
|
+
pos[o] = cp[0] + nx * s;
|
|
292
|
+
pos[o + 1] = cp[1] + ny * s;
|
|
293
|
+
pos[o + 2] = cp[2] + nz * s;
|
|
294
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime sprite atlas: `packAtlas` lays loose images out on one sheet (shelf packing, tallest
|
|
3
|
+
* first), `AtlasLayout.draw` paints them onto a 2D canvas context with their edges extruded into
|
|
4
|
+
* the padding, and `createAtlasTexture` does that into an `OffscreenCanvas` and uploads it with
|
|
5
|
+
* `loadTexture`. The packer is pure (no DOM), so Workers and Node tests run it on
|
|
6
|
+
* `{ width, height, data }` sources.
|
|
7
|
+
*/
|
|
8
|
+
import { type LoadTextureOptions, type Texture } from '../render/texture.js';
|
|
9
|
+
/** Tightly packed RGBA8 texels (`ImageData` is one). */
|
|
10
|
+
export interface AtlasPixels {
|
|
11
|
+
width: number;
|
|
12
|
+
height: number;
|
|
13
|
+
data: Uint8Array | Uint8ClampedArray;
|
|
14
|
+
}
|
|
15
|
+
/** An image the atlas can place: a decoded bitmap, a canvas, or raw RGBA8 texels. */
|
|
16
|
+
export type AtlasSource = ImageBitmap | HTMLCanvasElement | OffscreenCanvas | AtlasPixels;
|
|
17
|
+
export interface AtlasOptions {
|
|
18
|
+
/** Gap in px between two sprites' extruded slots, and nothing else (default 1). */
|
|
19
|
+
padding?: number;
|
|
20
|
+
/** Largest width or height the sheet may reach (default 4096); past it `packAtlas` throws. */
|
|
21
|
+
maxSize?: number;
|
|
22
|
+
/** Round the sheet's width and height up to powers of two (default true). */
|
|
23
|
+
powerOfTwo?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Pixels of each sprite's edge copied outward on every side (default 1), so linear filtering
|
|
26
|
+
* and mip reduction at a sprite's border never bleed a neighbour or the clear colour in.
|
|
27
|
+
*/
|
|
28
|
+
extrude?: number;
|
|
29
|
+
/**
|
|
30
|
+
* Order sprites tallest first, then widest, then by name (default true): the shelf packer's
|
|
31
|
+
* best case. `false` keeps the input order. Either way the result is deterministic.
|
|
32
|
+
*/
|
|
33
|
+
sort?: boolean;
|
|
34
|
+
}
|
|
35
|
+
/** Where one image sits: pixels (`x y w h`) and texture coordinates (`u0 v0 u1 v1`). */
|
|
36
|
+
export interface AtlasRect {
|
|
37
|
+
x: number;
|
|
38
|
+
y: number;
|
|
39
|
+
w: number;
|
|
40
|
+
h: number;
|
|
41
|
+
u0: number;
|
|
42
|
+
v0: number;
|
|
43
|
+
u1: number;
|
|
44
|
+
v1: number;
|
|
45
|
+
}
|
|
46
|
+
/** The subset of a 2D context `AtlasLayout.draw` uses (so tests can hand it a fake). */
|
|
47
|
+
export interface AtlasDrawContext {
|
|
48
|
+
drawImage(image: Exclude<AtlasSource, AtlasPixels>, sx: number, sy: number, sw: number, sh: number, dx: number, dy: number, dw: number, dh: number): void;
|
|
49
|
+
createImageData(width: number, height: number): ImageData;
|
|
50
|
+
putImageData(data: ImageData, dx: number, dy: number): void;
|
|
51
|
+
}
|
|
52
|
+
export interface AtlasLayout<N extends string = string> {
|
|
53
|
+
width: number;
|
|
54
|
+
height: number;
|
|
55
|
+
rects: Record<N, AtlasRect>;
|
|
56
|
+
/** The `extrude` the layout was packed with (what `draw` paints around each sprite). */
|
|
57
|
+
extrude: number;
|
|
58
|
+
/**
|
|
59
|
+
* Paint every image at its rect, with `extrude` px of its edge pixels copied outward on each
|
|
60
|
+
* side. Bitmaps and canvases go through `drawImage`; pixel sources are extruded in TS and
|
|
61
|
+
* written with `putImageData`.
|
|
62
|
+
*/
|
|
63
|
+
draw(ctx: AtlasDrawContext): void;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Lay `images` out on one sheet. Each sprite's slot is its size plus `extrude` on every side;
|
|
67
|
+
* slots are `padding` px apart and start at the sheet's top-left corner. Throws naming the image
|
|
68
|
+
* that does not fit when the sheet would exceed `maxSize`.
|
|
69
|
+
*/
|
|
70
|
+
export declare function packAtlas<N extends string>(images: Record<N, AtlasSource>, opts?: AtlasOptions): AtlasLayout<N>;
|
|
71
|
+
/**
|
|
72
|
+
* Copy `src` into the centre of an RGBA buffer `(w + 2e) × (h + 2e)` and fill the border with
|
|
73
|
+
* the nearest edge pixel (clamp-to-edge in software). Exported for tests and custom drawers.
|
|
74
|
+
*/
|
|
75
|
+
export declare function extrudePixels(src: AtlasPixels, e: number, out: Uint8ClampedArray | Uint8Array): void;
|
|
76
|
+
export interface AtlasTextureOptions extends LoadTextureOptions {
|
|
77
|
+
/** The canvas to draw into (default a new `OffscreenCanvas` of the layout's size). */
|
|
78
|
+
canvas?: OffscreenCanvas | HTMLCanvasElement;
|
|
79
|
+
}
|
|
80
|
+
/** The atlas on the GPU: `createAtlasTexture`'s result. */
|
|
81
|
+
export interface AtlasTexture<N extends string = string> {
|
|
82
|
+
texture: Texture;
|
|
83
|
+
rects: Record<N, AtlasRect>;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Draw `layout` into an `OffscreenCanvas` and upload it with `loadTexture` (sRGB, mips on by
|
|
87
|
+
* default; pass `mips: false` for pixel art, with `filter` or `format` as `loadTexture` takes).
|
|
88
|
+
* Allocates; call it at load time.
|
|
89
|
+
*/
|
|
90
|
+
export declare function createAtlasTexture<N extends string>(device: GPUDevice, layout: AtlasLayout<N>, images: Record<N, AtlasSource>, opts?: AtlasTextureOptions): AtlasTexture<N>;
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime sprite atlas: `packAtlas` lays loose images out on one sheet (shelf packing, tallest
|
|
3
|
+
* first), `AtlasLayout.draw` paints them onto a 2D canvas context with their edges extruded into
|
|
4
|
+
* the padding, and `createAtlasTexture` does that into an `OffscreenCanvas` and uploads it with
|
|
5
|
+
* `loadTexture`. The packer is pure (no DOM), so Workers and Node tests run it on
|
|
6
|
+
* `{ width, height, data }` sources.
|
|
7
|
+
*/
|
|
8
|
+
import { loadTexture } from '../render/texture.js';
|
|
9
|
+
function isPixels(s) {
|
|
10
|
+
return 'data' in s && ArrayBuffer.isView(s.data);
|
|
11
|
+
}
|
|
12
|
+
function nextPow2(n) {
|
|
13
|
+
let p = 1;
|
|
14
|
+
while (p < n)
|
|
15
|
+
p *= 2;
|
|
16
|
+
return p;
|
|
17
|
+
}
|
|
18
|
+
/** Shelf-pack `slots` (in order) into `width`; returns positions and the height used. */
|
|
19
|
+
function shelfPack(slots, width, padding) {
|
|
20
|
+
const pos = [];
|
|
21
|
+
let x = 0;
|
|
22
|
+
let y = 0;
|
|
23
|
+
let shelf = 0;
|
|
24
|
+
let used = 0;
|
|
25
|
+
for (const s of slots) {
|
|
26
|
+
if (s.w > width)
|
|
27
|
+
return { pos, height: 0, used, overflow: s.name };
|
|
28
|
+
if (x > 0 && x + s.w > width) {
|
|
29
|
+
x = 0;
|
|
30
|
+
y += shelf + padding;
|
|
31
|
+
shelf = 0;
|
|
32
|
+
}
|
|
33
|
+
pos.push({ x, y });
|
|
34
|
+
x += s.w + padding;
|
|
35
|
+
if (x - padding > used)
|
|
36
|
+
used = x - padding;
|
|
37
|
+
if (s.h > shelf)
|
|
38
|
+
shelf = s.h;
|
|
39
|
+
}
|
|
40
|
+
return { pos, height: slots.length ? y + shelf : 0, used, overflow: null };
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Lay `images` out on one sheet. Each sprite's slot is its size plus `extrude` on every side;
|
|
44
|
+
* slots are `padding` px apart and start at the sheet's top-left corner. Throws naming the image
|
|
45
|
+
* that does not fit when the sheet would exceed `maxSize`.
|
|
46
|
+
*/
|
|
47
|
+
export function packAtlas(images, opts = {}) {
|
|
48
|
+
const padding = Math.max(0, Math.floor(opts.padding ?? 1));
|
|
49
|
+
const maxSize = Math.max(1, Math.floor(opts.maxSize ?? 4096));
|
|
50
|
+
const extrude = Math.max(0, Math.floor(opts.extrude ?? 1));
|
|
51
|
+
const pow2 = opts.powerOfTwo ?? true;
|
|
52
|
+
const sort = opts.sort ?? true;
|
|
53
|
+
const names = Object.keys(images);
|
|
54
|
+
const slots = names.map((name) => {
|
|
55
|
+
const img = images[name];
|
|
56
|
+
if (!(img.width > 0 && img.height > 0)) {
|
|
57
|
+
throw new Error(`packAtlas: image "${name}" is ${img.width}×${img.height}`);
|
|
58
|
+
}
|
|
59
|
+
return { name, w: img.width + 2 * extrude, h: img.height + 2 * extrude };
|
|
60
|
+
});
|
|
61
|
+
if (sort) {
|
|
62
|
+
slots.sort((a, b) => b.h - a.h || b.w - a.w || (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
63
|
+
}
|
|
64
|
+
let area = 0;
|
|
65
|
+
let widest = 0;
|
|
66
|
+
for (const s of slots) {
|
|
67
|
+
area += (s.w + padding) * (s.h + padding);
|
|
68
|
+
if (s.w > widest)
|
|
69
|
+
widest = s.w;
|
|
70
|
+
}
|
|
71
|
+
const roundUp = (n) => (pow2 ? nextPow2(n) : Math.ceil(n));
|
|
72
|
+
if (widest > maxSize) {
|
|
73
|
+
const s = slots.find((s) => s.w === widest);
|
|
74
|
+
throw new Error(`packAtlas: image "${s.name}" is ${s.w}px wide with its extrusion; maxSize is ${maxSize}`);
|
|
75
|
+
}
|
|
76
|
+
let width = Math.min(maxSize, roundUp(Math.max(widest, Math.sqrt(area))));
|
|
77
|
+
let packed = shelfPack(slots, width, padding);
|
|
78
|
+
let height = roundUp(packed.height);
|
|
79
|
+
// A sheet taller than it is wide (or than maxSize) is widened until it fits or cannot grow.
|
|
80
|
+
while ((height > width || height > maxSize) && width < maxSize) {
|
|
81
|
+
width = Math.min(maxSize, pow2 ? width * 2 : Math.ceil(width * 1.25));
|
|
82
|
+
packed = shelfPack(slots, width, padding);
|
|
83
|
+
height = roundUp(packed.height);
|
|
84
|
+
}
|
|
85
|
+
if (packed.overflow !== null || height > maxSize) {
|
|
86
|
+
// The first slot whose bottom edge crosses maxSize is the one that does not fit.
|
|
87
|
+
let culprit = packed.overflow;
|
|
88
|
+
if (culprit === null) {
|
|
89
|
+
for (let i = 0; i < slots.length; i++) {
|
|
90
|
+
if (packed.pos[i].y + slots[i].h > maxSize) {
|
|
91
|
+
culprit = slots[i].name;
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
throw new Error(`packAtlas: image "${culprit}" does not fit: ${slots.length} images need more than ${maxSize}×${maxSize}`);
|
|
97
|
+
}
|
|
98
|
+
// The sheet is only as wide as its widest shelf (rounded up when powerOfTwo).
|
|
99
|
+
width = Math.max(1, roundUp(packed.used));
|
|
100
|
+
if (height < 1)
|
|
101
|
+
height = 1;
|
|
102
|
+
const rects = {};
|
|
103
|
+
slots.forEach((s, i) => {
|
|
104
|
+
const p = packed.pos[i];
|
|
105
|
+
const x = p.x + extrude;
|
|
106
|
+
const y = p.y + extrude;
|
|
107
|
+
const w = s.w - 2 * extrude;
|
|
108
|
+
const h = s.h - 2 * extrude;
|
|
109
|
+
rects[s.name] = {
|
|
110
|
+
x,
|
|
111
|
+
y,
|
|
112
|
+
w,
|
|
113
|
+
h,
|
|
114
|
+
u0: x / width,
|
|
115
|
+
v0: y / height,
|
|
116
|
+
u1: (x + w) / width,
|
|
117
|
+
v1: (y + h) / height,
|
|
118
|
+
};
|
|
119
|
+
});
|
|
120
|
+
return {
|
|
121
|
+
width,
|
|
122
|
+
height,
|
|
123
|
+
rects,
|
|
124
|
+
extrude,
|
|
125
|
+
draw(ctx) {
|
|
126
|
+
for (const name of names)
|
|
127
|
+
drawOne(ctx, images[name], rects[name], extrude);
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
function drawOne(ctx, img, r, e) {
|
|
132
|
+
if (isPixels(img)) {
|
|
133
|
+
const out = ctx.createImageData(r.w + 2 * e, r.h + 2 * e);
|
|
134
|
+
extrudePixels(img, e, out.data);
|
|
135
|
+
ctx.putImageData(out, r.x - e, r.y - e);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const { w, h } = r;
|
|
139
|
+
ctx.drawImage(img, 0, 0, w, h, r.x, r.y, w, h);
|
|
140
|
+
if (e <= 0)
|
|
141
|
+
return;
|
|
142
|
+
const x0 = r.x - e;
|
|
143
|
+
const y0 = r.y - e;
|
|
144
|
+
const x1 = r.x + w;
|
|
145
|
+
const y1 = r.y + h;
|
|
146
|
+
// Edges: a one-pixel strip of the source stretched across the extrusion.
|
|
147
|
+
ctx.drawImage(img, 0, 0, w, 1, r.x, y0, w, e); // top
|
|
148
|
+
ctx.drawImage(img, 0, h - 1, w, 1, r.x, y1, w, e); // bottom
|
|
149
|
+
ctx.drawImage(img, 0, 0, 1, h, x0, r.y, e, h); // left
|
|
150
|
+
ctx.drawImage(img, w - 1, 0, 1, h, x1, r.y, e, h); // right
|
|
151
|
+
// Corners: one source pixel each.
|
|
152
|
+
ctx.drawImage(img, 0, 0, 1, 1, x0, y0, e, e);
|
|
153
|
+
ctx.drawImage(img, w - 1, 0, 1, 1, x1, y0, e, e);
|
|
154
|
+
ctx.drawImage(img, 0, h - 1, 1, 1, x0, y1, e, e);
|
|
155
|
+
ctx.drawImage(img, w - 1, h - 1, 1, 1, x1, y1, e, e);
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Copy `src` into the centre of an RGBA buffer `(w + 2e) × (h + 2e)` and fill the border with
|
|
159
|
+
* the nearest edge pixel (clamp-to-edge in software). Exported for tests and custom drawers.
|
|
160
|
+
*/
|
|
161
|
+
export function extrudePixels(src, e, out) {
|
|
162
|
+
const { width: w, height: h, data } = src;
|
|
163
|
+
const W = w + 2 * e;
|
|
164
|
+
const H = h + 2 * e;
|
|
165
|
+
if (out.length < W * H * 4)
|
|
166
|
+
throw new Error('extrudePixels: output too small');
|
|
167
|
+
for (let y = 0; y < H; y++) {
|
|
168
|
+
const sy = Math.min(h - 1, Math.max(0, y - e));
|
|
169
|
+
for (let x = 0; x < W; x++) {
|
|
170
|
+
const sx = Math.min(w - 1, Math.max(0, x - e));
|
|
171
|
+
const si = (sy * w + sx) * 4;
|
|
172
|
+
const di = (y * W + x) * 4;
|
|
173
|
+
out[di] = data[si];
|
|
174
|
+
out[di + 1] = data[si + 1];
|
|
175
|
+
out[di + 2] = data[si + 2];
|
|
176
|
+
out[di + 3] = data[si + 3];
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Draw `layout` into an `OffscreenCanvas` and upload it with `loadTexture` (sRGB, mips on by
|
|
182
|
+
* default; pass `mips: false` for pixel art, with `filter` or `format` as `loadTexture` takes).
|
|
183
|
+
* Allocates; call it at load time.
|
|
184
|
+
*/
|
|
185
|
+
export function createAtlasTexture(device, layout, images, opts = {}) {
|
|
186
|
+
const { canvas: given, ...texOpts } = opts;
|
|
187
|
+
const canvas = given ?? new OffscreenCanvas(layout.width, layout.height);
|
|
188
|
+
if (canvas.width !== layout.width || canvas.height !== layout.height) {
|
|
189
|
+
canvas.width = layout.width;
|
|
190
|
+
canvas.height = layout.height;
|
|
191
|
+
}
|
|
192
|
+
const ctx = canvas.getContext('2d');
|
|
193
|
+
if (!ctx)
|
|
194
|
+
throw new Error('createAtlasTexture: no 2d context');
|
|
195
|
+
// `images` is the layout's own source map; a different one with the same names draws too.
|
|
196
|
+
packLayoutWith(layout, images).draw(ctx);
|
|
197
|
+
const texture = loadTexture(device, canvas, { label: 'atlas', ...texOpts });
|
|
198
|
+
return { texture, rects: layout.rects };
|
|
199
|
+
}
|
|
200
|
+
/** The layout's `draw` over `images` (the same object it was packed from, or a stand-in). */
|
|
201
|
+
function packLayoutWith(layout, images) {
|
|
202
|
+
return {
|
|
203
|
+
draw(ctx) {
|
|
204
|
+
for (const name of Object.keys(layout.rects)) {
|
|
205
|
+
const img = images[name];
|
|
206
|
+
if (!img)
|
|
207
|
+
throw new Error(`createAtlasTexture: no image for "${name}"`);
|
|
208
|
+
drawOne(ctx, img, layout.rects[name], layout.extrude);
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
};
|
|
212
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The manifest `pnpm bake` (`scripts/bake.mjs`) writes next to its baked bytes: what was baked
|
|
3
|
+
* from which sources, with a hash of the inputs. A game reads it to know which `.navm` and
|
|
4
|
+
* `.coll` files exist (and to decide whether to fall back to a runtime bake).
|
|
5
|
+
*/
|
|
6
|
+
/** What one baked file is. */
|
|
7
|
+
export type BakeKind = 'nav' | 'collider';
|
|
8
|
+
export interface BakeManifestEntry {
|
|
9
|
+
/** The config entry's `name`: the file is `<name>.navm` or `<name>.coll` beside the manifest. */
|
|
10
|
+
name: string;
|
|
11
|
+
kind: BakeKind;
|
|
12
|
+
/** The source files, as the config named them. */
|
|
13
|
+
sources: string[];
|
|
14
|
+
/** Hex SHA-256 over the sources' bytes and the entry's options. */
|
|
15
|
+
sha256: string;
|
|
16
|
+
/** Size of the baked file. */
|
|
17
|
+
bytes: number;
|
|
18
|
+
/** Bake time, milliseconds. */
|
|
19
|
+
ms: number;
|
|
20
|
+
}
|
|
21
|
+
export interface BakeManifest {
|
|
22
|
+
version: number;
|
|
23
|
+
/** ISO 8601. */
|
|
24
|
+
bakedAt: string;
|
|
25
|
+
entries: BakeManifestEntry[];
|
|
26
|
+
}
|
|
27
|
+
/** The manifest format this reader accepts. */
|
|
28
|
+
export declare const BAKE_MANIFEST_VERSION = 1;
|
|
29
|
+
/**
|
|
30
|
+
* Parse `bake-manifest.json` (a string or already-parsed JSON). Throws on another format version
|
|
31
|
+
* or a malformed entry, so a stale manifest fails loudly instead of naming files that are not there.
|
|
32
|
+
*/
|
|
33
|
+
export declare function readBakeManifest(json: string | unknown): BakeManifest;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The manifest `pnpm bake` (`scripts/bake.mjs`) writes next to its baked bytes: what was baked
|
|
3
|
+
* from which sources, with a hash of the inputs. A game reads it to know which `.navm` and
|
|
4
|
+
* `.coll` files exist (and to decide whether to fall back to a runtime bake).
|
|
5
|
+
*/
|
|
6
|
+
/** The manifest format this reader accepts. */
|
|
7
|
+
export const BAKE_MANIFEST_VERSION = 1;
|
|
8
|
+
/**
|
|
9
|
+
* Parse `bake-manifest.json` (a string or already-parsed JSON). Throws on another format version
|
|
10
|
+
* or a malformed entry, so a stale manifest fails loudly instead of naming files that are not there.
|
|
11
|
+
*/
|
|
12
|
+
export function readBakeManifest(json) {
|
|
13
|
+
const raw = typeof json === 'string' ? JSON.parse(json) : json;
|
|
14
|
+
if (!raw || typeof raw !== 'object')
|
|
15
|
+
throw new Error('bake-manifest: not an object');
|
|
16
|
+
const m = raw;
|
|
17
|
+
if (m.version !== BAKE_MANIFEST_VERSION) {
|
|
18
|
+
throw new Error(`bake-manifest: version ${String(m.version)}, expected ${BAKE_MANIFEST_VERSION}`);
|
|
19
|
+
}
|
|
20
|
+
if (!Array.isArray(m.entries))
|
|
21
|
+
throw new Error('bake-manifest: entries is not an array');
|
|
22
|
+
const entries = m.entries.map((e, i) => {
|
|
23
|
+
const r = (e ?? {});
|
|
24
|
+
const kind = r.kind;
|
|
25
|
+
if (typeof r.name !== 'string' || (kind !== 'nav' && kind !== 'collider')) {
|
|
26
|
+
throw new Error(`bake-manifest: entry ${i} has no name or an unknown kind`);
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
name: r.name,
|
|
30
|
+
kind,
|
|
31
|
+
sources: Array.isArray(r.sources) ? r.sources.map(String) : [],
|
|
32
|
+
sha256: typeof r.sha256 === 'string' ? r.sha256 : '',
|
|
33
|
+
bytes: typeof r.bytes === 'number' ? r.bytes : 0,
|
|
34
|
+
ms: typeof r.ms === 'number' ? r.ms : 0,
|
|
35
|
+
};
|
|
36
|
+
});
|
|
37
|
+
return { version: BAKE_MANIFEST_VERSION, bakedAt: String(m.bakedAt ?? ''), entries };
|
|
38
|
+
}
|