@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,530 @@
|
|
|
1
|
+
/// <reference path="../platform/xr-types.d.ts" preserve="true" />
|
|
2
|
+
/**
|
|
3
|
+
* `createGame` (D-041 Phase 1): the noun-free wiring every ThunderClap game starts from.
|
|
4
|
+
*
|
|
5
|
+
* It blocks browser zoom, boots WebGPU on a letterboxed surface and builds, in this order:
|
|
6
|
+
* renderer, post stack, particles, host, the game's save (if any), overlay and pad navigation,
|
|
7
|
+
* audio (mixer persisted through the host), the game's tick clock, input with its gestures (on
|
|
8
|
+
* that clock) and pointer router, the scene stack and the frame loop (frames come from the
|
|
9
|
+
* host). It also wires the page lifecycle (hide, pagehide and freeze stop frames and flush the
|
|
10
|
+
* save; show resumes), the host's app lifecycle (`background` stops frames and flushes the save,
|
|
11
|
+
* `active` resumes) and device loss (the reload screen, then loop, audio and captions halt for
|
|
12
|
+
* good).
|
|
13
|
+
*
|
|
14
|
+
* The surface letterboxes the design space; what the overlay's layout leaves free of UI is
|
|
15
|
+
* `ctx.ui.layout.freeRect`, and a 2D game frames its world into it with
|
|
16
|
+
* `ctx.camera.fitFreeRect(worldRect, ctx.ui.layout.freeRect, ctx.surface)`. Scenes draw through
|
|
17
|
+
* `ctx.camera` (`Scene.render(frame, camera, alpha)`); the loop calls
|
|
18
|
+
* `renderer.render(frame, ctx.camera)` once per frame.
|
|
19
|
+
*
|
|
20
|
+
* Two parts are opt-in, so a pure client of a server-authoritative game (VISION pillar 6) uses
|
|
21
|
+
* the same wiring: `save` (omit it and `ctx.save` is null, and nothing touches `host.storage`)
|
|
22
|
+
* and the fixed-step sim (`sim: false` runs the loop at render rate, `hooks.frame` each frame).
|
|
23
|
+
*
|
|
24
|
+
* Games add their nouns by extending the returned object (`Object.assign(ctx, { ... })`): the
|
|
25
|
+
* scene stack hands scenes this same object, so a scene typed against the game's own ctx sees
|
|
26
|
+
* them. Game logic plugs into the frame and the lifecycle through `ctx.hooks`.
|
|
27
|
+
*
|
|
28
|
+
* Headless (ws/core-8): given `gpu` (a fake device in a test) `createGame` skips the WebGPU
|
|
29
|
+
* boot and its fatal screens and mounts the surface without a GPU canvas context; with `clock`
|
|
30
|
+
* the game runs on that tick clock and with `host` on that host's frames. `root` may then be
|
|
31
|
+
* left out. The overlay still needs a DOM (happy-dom in Vitest). `@thunderclap-engine/engine/testing`
|
|
32
|
+
* `createTestGame` wires the fakes in.
|
|
33
|
+
*
|
|
34
|
+
* VR is one call (`ctx.xr.enter()`, ws/xr): frames then come from the session through an XR
|
|
35
|
+
* host, the scenes record once through the surface's head camera and the renderer draws each
|
|
36
|
+
* eye into the surface's HDR scene target and resolves it through the post stack into the
|
|
37
|
+
* session's layer (ws/xr-post); controllers and hands join `ctx.pointers`. Leaving (`exit()` or
|
|
38
|
+
* the session ending) puts everything back; a game that never enters runs the same code as before.
|
|
39
|
+
*/
|
|
40
|
+
import { AudioEngine, persistMixer } from '../audio/index.js';
|
|
41
|
+
import { ParticleSystem } from '../fx/index.js';
|
|
42
|
+
import { createWebHost, createXrHost, } from '../host/index.js';
|
|
43
|
+
import { createGestures, createInputPlayback, createInputRecorder, createPointerRouter, createTickClock, Input, XrPointerSource, } from '../input/index.js';
|
|
44
|
+
import { blockBrowserZoom, boot, mountSurface, watchLifecycle, XrSurface, } from '../platform/index.js';
|
|
45
|
+
import { PostStack } from '../post/index.js';
|
|
46
|
+
import { installGpuErrorLogging, Renderer, } from '../render/index.js';
|
|
47
|
+
import { applyTextScale, createOverlay, followSafeArea, PadNav, } from '../ui/index.js';
|
|
48
|
+
import { DetachedSurface, NO_DOM } from './detached-surface.js';
|
|
49
|
+
import { GameLoop } from './loop.js';
|
|
50
|
+
import { SceneStack } from './scene.js';
|
|
51
|
+
/**
|
|
52
|
+
* Boots WebGPU and builds the engine ctx. Resolves `null` when WebGPU is unavailable: the
|
|
53
|
+
* engine has already shown the unsupported screen and there is nothing more to do.
|
|
54
|
+
*
|
|
55
|
+
* Nothing runs until the game calls `ctx.loop.start()`, except the lifecycle handlers (a show
|
|
56
|
+
* starts the loop) and resize, which keeps the renderer and overlay in step.
|
|
57
|
+
*/
|
|
58
|
+
export async function createGame(opts) {
|
|
59
|
+
const design = { width: opts.design?.width ?? 1920, height: opts.design?.height ?? 1080 };
|
|
60
|
+
const surfaceOpts = {
|
|
61
|
+
...opts.surface,
|
|
62
|
+
logicalWidth: design.width,
|
|
63
|
+
logicalHeight: design.height,
|
|
64
|
+
};
|
|
65
|
+
// `overlay: false` (ws/core-9): no DOM is touched from here on.
|
|
66
|
+
const dom = opts.overlay !== false;
|
|
67
|
+
let res;
|
|
68
|
+
if (!dom) {
|
|
69
|
+
if (!opts.gpu || !opts.host) {
|
|
70
|
+
throw new Error('createGame: `overlay: false` needs `gpu` and `host` (it touches no DOM)');
|
|
71
|
+
}
|
|
72
|
+
res = gpuBoot(new DetachedSurface(surfaceOpts), opts.gpu);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
const root = opts.root ?? (opts.gpu ? globalThis.document?.createElement('div') : undefined);
|
|
76
|
+
if (!root) {
|
|
77
|
+
throw new Error('createGame: `root` is required (a headless game passes `gpu` and a DOM, or `overlay: false`)');
|
|
78
|
+
}
|
|
79
|
+
// Before boot, so it holds from the first frame (and on the fatal screens too).
|
|
80
|
+
if (opts.blockBrowserZoom !== false)
|
|
81
|
+
blockBrowserZoom(root.ownerDocument);
|
|
82
|
+
const booted = opts.gpu
|
|
83
|
+
? gpuBoot(mountSurface(root, surfaceOpts), opts.gpu)
|
|
84
|
+
: await boot(root, {
|
|
85
|
+
...surfaceOpts,
|
|
86
|
+
title: opts.title,
|
|
87
|
+
forceUnsupported: opts.forceUnsupported,
|
|
88
|
+
});
|
|
89
|
+
if (!booted.ok)
|
|
90
|
+
return null;
|
|
91
|
+
res = booted;
|
|
92
|
+
}
|
|
93
|
+
const { surface, gpu } = res;
|
|
94
|
+
opts.onGpuReady?.(gpu);
|
|
95
|
+
installGpuErrorLogging(gpu.device);
|
|
96
|
+
// ── GPU ────────────────────────────────────────────────────────────────
|
|
97
|
+
const renderer = new Renderer(gpu, {
|
|
98
|
+
logicalWidth: design.width,
|
|
99
|
+
logicalHeight: design.height,
|
|
100
|
+
aux: opts.aux,
|
|
101
|
+
});
|
|
102
|
+
const post = new PostStack(renderer, opts.post);
|
|
103
|
+
renderer.post = post;
|
|
104
|
+
const particles = new ParticleSystem(renderer, opts.particles);
|
|
105
|
+
// ── Host, save + overlay ───────────────────────────────────────────────
|
|
106
|
+
const host = opts.host ?? createWebHost();
|
|
107
|
+
// No save, no storage access: a pure client keeps nothing on the device.
|
|
108
|
+
const save = (opts.save ? opts.save(host) : null);
|
|
109
|
+
let ui = null;
|
|
110
|
+
if (dom) {
|
|
111
|
+
opts.styles?.(surface.root.ownerDocument);
|
|
112
|
+
// When a panel closes and its opener can't take focus back, focus returns to the canvas so
|
|
113
|
+
// keys keep reaching the game.
|
|
114
|
+
const overlay = createOverlay({
|
|
115
|
+
parent: surface.root,
|
|
116
|
+
designWidth: design.width,
|
|
117
|
+
designHeight: design.height,
|
|
118
|
+
layout: opts.layout,
|
|
119
|
+
focusHome: () => surface.canvas,
|
|
120
|
+
theme: opts.theme,
|
|
121
|
+
// `ui.anchors()` and `ui.floats()` without a camera follow the game's (ws/api-core).
|
|
122
|
+
camera: () => renderer.camera,
|
|
123
|
+
});
|
|
124
|
+
if (opts.textScale !== undefined && opts.textScale !== 1) {
|
|
125
|
+
applyTextScale(overlay.root, opts.textScale, (t) => overlay.setTheme(t));
|
|
126
|
+
}
|
|
127
|
+
// A host that knows the notch and home indicator pads the layout's regions by them.
|
|
128
|
+
if (host.safeArea)
|
|
129
|
+
followSafeArea(overlay.layout, host.safeArea);
|
|
130
|
+
ui = overlay;
|
|
131
|
+
}
|
|
132
|
+
// Input is blocked while a modal panel is up, so the pad drives panels through this. With no
|
|
133
|
+
// overlay there are no panels: the pad stays the game's.
|
|
134
|
+
const padNav = new PadNav(ui ? ui.stack : NO_PANELS, { getGamepads: opts.input.getGamepads });
|
|
135
|
+
surface.onResize(({ rect, backbuffer }) => {
|
|
136
|
+
renderer.resize(backbuffer.width, backbuffer.height);
|
|
137
|
+
ui?.setFrame(rect);
|
|
138
|
+
});
|
|
139
|
+
// ── Audio ──────────────────────────────────────────────────────────────
|
|
140
|
+
// Unlocks on the first gesture and pauses itself while the page is hidden (see attach).
|
|
141
|
+
// Without a DOM nothing unlocks it: it stays locked and silent.
|
|
142
|
+
const audio = new AudioEngine();
|
|
143
|
+
if (dom)
|
|
144
|
+
audio.attach();
|
|
145
|
+
if (opts.mixerKey)
|
|
146
|
+
persistMixer(audio, { game: opts.mixerKey, host });
|
|
147
|
+
// ── Input (blocked under modal panels) ─────────────────────────────────
|
|
148
|
+
const extraBlock = opts.input.blocked;
|
|
149
|
+
// Gestures and pointer rays link to the input: same block, same transform, same attach,
|
|
150
|
+
// advanced in `input.update()`. The rays go through whatever camera the renderer draws with:
|
|
151
|
+
// the head while a VR session runs (so a head-locked `WorldPanel` faces the headset).
|
|
152
|
+
// The gestures run on the game's tick clock (advanced by the loop below), so their timers
|
|
153
|
+
// are game time and a replay lands them on the same ticks.
|
|
154
|
+
const clock = opts.clock ?? createTickClock();
|
|
155
|
+
const gestures = createGestures({ magnify: opts.input.magnify, clock });
|
|
156
|
+
// While a session runs: its host (frames and the current `XRFrame`) and its surface (eyes).
|
|
157
|
+
let xrHost = null;
|
|
158
|
+
let xrSurface = null;
|
|
159
|
+
const pointers = createPointerRouter({
|
|
160
|
+
camera: () => xrSurface?.head ?? renderer.camera,
|
|
161
|
+
testers: opts.pointers?.testers,
|
|
162
|
+
});
|
|
163
|
+
const panels = ui?.stack;
|
|
164
|
+
const modal = panels ? () => panels.hasModal : () => false;
|
|
165
|
+
const input = new Input(opts.input.bindings, {
|
|
166
|
+
blocked: extraBlock ? () => modal() || extraBlock() : modal,
|
|
167
|
+
globalKeys: opts.input.globalKeys,
|
|
168
|
+
buttonKeys: opts.input.buttonKeys,
|
|
169
|
+
getGamepads: opts.input.getGamepads,
|
|
170
|
+
gestures,
|
|
171
|
+
pointers,
|
|
172
|
+
});
|
|
173
|
+
// A replay feeds the input itself: no DOM listeners, so a real key never leaks in (E-011).
|
|
174
|
+
// The playback drives the game's clock (ws/input-9): each fed tick advances it by the
|
|
175
|
+
// recording's tick spacing, so `playback.speed` above 1 keeps game time too.
|
|
176
|
+
const playback = opts.input.replay
|
|
177
|
+
? createInputPlayback(input, opts.input.replay, { clock })
|
|
178
|
+
: null;
|
|
179
|
+
if (opts.input.record)
|
|
180
|
+
createInputRecorder(input);
|
|
181
|
+
// The recorded pointers are dispatched through a pointer target nothing real can reach (an
|
|
182
|
+
// element outside the page that keys are read from too), so the gestures, rays and any look
|
|
183
|
+
// see them on their recorded ticks and never a real pointer or key.
|
|
184
|
+
// Its box is the canvas's (ws/core-8), so a touch stick or touch zone, which measures the
|
|
185
|
+
// pointer against the box, replays where it was recorded.
|
|
186
|
+
// With `overlay: false` there is nothing to attach to: `detach()` only releases what is held.
|
|
187
|
+
const sink = dom ? surface.root.ownerDocument.createElement('div') : null;
|
|
188
|
+
const attachSink = sink
|
|
189
|
+
? () => input.attach({ pointerTarget: sink, keyTarget: sink, bounds: surface.canvas })
|
|
190
|
+
: () => input.detach();
|
|
191
|
+
// Where the input lives outside a `ctx.replay`: the canvas, or the sink in replay mode.
|
|
192
|
+
const attachHome = playback || !sink ? attachSink : () => input.attach({ pointerTarget: surface.canvas });
|
|
193
|
+
attachHome();
|
|
194
|
+
input.setPointerTransform((x, y) => surface.clientToLogical(x, y));
|
|
195
|
+
// Overlay controls (ws/input-12): every button click reaches the input by its stable id, so a
|
|
196
|
+
// recording carries it; a replay's `ui` lane comes back as `'control'` and clicks the button
|
|
197
|
+
// with that id before the tick's update, on the tick it was clicked live.
|
|
198
|
+
if (ui) {
|
|
199
|
+
const overlay = ui;
|
|
200
|
+
overlay.onControl((id) => input.control(id));
|
|
201
|
+
input.on('control', (id) => {
|
|
202
|
+
overlay.activate(id);
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
/** The playback `ctx.replay` installed, until it ends or is stopped. */
|
|
206
|
+
let replaying = null;
|
|
207
|
+
/** Its `onEnd`, cleared by `stop()` so a stopped or replaced replay never reports an end. */
|
|
208
|
+
let replayEnd;
|
|
209
|
+
const replay = (recording, ropts = {}) => {
|
|
210
|
+
replaying?.stop();
|
|
211
|
+
input.playback?.detach();
|
|
212
|
+
// `attach` releases every key and button first, so nothing the player held leaks in.
|
|
213
|
+
attachSink();
|
|
214
|
+
// A live pointer source (the screen, an XR controller) is muted for the replay too: the
|
|
215
|
+
// sink stops the DOM ones, the mute the rest. Its remover lifts only this mute, so a mute
|
|
216
|
+
// the game set stays.
|
|
217
|
+
const unmute = pointers.mute();
|
|
218
|
+
const pb = createInputPlayback(input, recording, {
|
|
219
|
+
speed: ropts.speed,
|
|
220
|
+
tickMs: ropts.tickMs,
|
|
221
|
+
clock,
|
|
222
|
+
});
|
|
223
|
+
const uninstall = pb.detach;
|
|
224
|
+
pb.stop = pb.detach = () => {
|
|
225
|
+
uninstall();
|
|
226
|
+
unmute();
|
|
227
|
+
if (replaying !== pb)
|
|
228
|
+
return;
|
|
229
|
+
replaying = null;
|
|
230
|
+
replayEnd = undefined;
|
|
231
|
+
// Releases the recording's last held keys and buttons too.
|
|
232
|
+
attachHome();
|
|
233
|
+
};
|
|
234
|
+
replaying = pb;
|
|
235
|
+
replayEnd = ropts.onEnd;
|
|
236
|
+
return pb;
|
|
237
|
+
};
|
|
238
|
+
// ── Scenes + loop ──────────────────────────────────────────────────────
|
|
239
|
+
const hooks = {};
|
|
240
|
+
// The stack hands scenes this object on enter; the game fills in its nouns before then.
|
|
241
|
+
const ctx = {};
|
|
242
|
+
const stack = new SceneStack(ctx);
|
|
243
|
+
// With `sim: false` the loop calls update once per rendered frame with the real delta.
|
|
244
|
+
const fixed = opts.sim !== false;
|
|
245
|
+
const loop = new GameLoop({
|
|
246
|
+
update: (dt) => {
|
|
247
|
+
// A replay feeds this tick's events first: live, they arrived between the frames, while
|
|
248
|
+
// the clock still read the last tick. Then the clock moves by the step, before the
|
|
249
|
+
// input (and its gestures) update. A playback driving the clock has already moved it
|
|
250
|
+
// for each tick it fed; once the recording is done the step moves it again. The playback
|
|
251
|
+
// is read from the input each step, so one the game installs mid-session (a replay
|
|
252
|
+
// started from a menu: `createInputPlayback(ctx.input, rec, { clock: ctx.clock })`) is
|
|
253
|
+
// stepped here too, in the same order (ws/core-5).
|
|
254
|
+
// A `ctx.replay` whose recording ran out last step gives the input back here, before
|
|
255
|
+
// this step's update, so its last tick was updated with the replayed state (ws/core-6).
|
|
256
|
+
const pb = input.playback;
|
|
257
|
+
const fed = pb ? pb.step() : false;
|
|
258
|
+
if (!fed && replaying !== null && pb === replaying) {
|
|
259
|
+
const onEnd = replayEnd;
|
|
260
|
+
replaying.stop();
|
|
261
|
+
onEnd?.();
|
|
262
|
+
}
|
|
263
|
+
if (!fed || pb?.clock !== clock)
|
|
264
|
+
clock.advance(dt * 1000);
|
|
265
|
+
input.update();
|
|
266
|
+
padNav.update();
|
|
267
|
+
audio.update(dt);
|
|
268
|
+
if (fixed)
|
|
269
|
+
hooks.update?.(dt);
|
|
270
|
+
else
|
|
271
|
+
hooks.frame?.(dt);
|
|
272
|
+
particles.update(dt);
|
|
273
|
+
stack.update(dt);
|
|
274
|
+
},
|
|
275
|
+
render: (alpha, time) => {
|
|
276
|
+
hooks.beforeRender?.(time);
|
|
277
|
+
const frame = renderer.beginFrame(time.realTime, time.frameDt);
|
|
278
|
+
if (xrSurface && xrHost) {
|
|
279
|
+
// The scenes record once, through the head; each eye draws the record into the
|
|
280
|
+
// surface's HDR scene target and the post stack resolves it into the eye's part of
|
|
281
|
+
// the layer (ws/xr-post), so the headset sees what the screen would.
|
|
282
|
+
const xrFrame = xrHost.frame;
|
|
283
|
+
const eyes = xrFrame ? xrSurface.views(xrFrame) : NO_EYES;
|
|
284
|
+
stack.render(frame, xrSurface.head, alpha);
|
|
285
|
+
for (let i = 0; i < eyes.length; i++) {
|
|
286
|
+
const eye = eyes[i];
|
|
287
|
+
const scene = xrSurface.scene;
|
|
288
|
+
renderer.render(frame, eye.camera, scene);
|
|
289
|
+
renderer.resolve(scene, eye.target.color.view, eye.viewport);
|
|
290
|
+
}
|
|
291
|
+
renderer.endFrame({ present: false });
|
|
292
|
+
}
|
|
293
|
+
else {
|
|
294
|
+
// Read each frame: `ctx.setCamera` swaps it.
|
|
295
|
+
const camera = renderer.camera;
|
|
296
|
+
stack.render(frame, camera, alpha);
|
|
297
|
+
renderer.render(frame, camera);
|
|
298
|
+
renderer.endFrame();
|
|
299
|
+
}
|
|
300
|
+
hooks.afterRender?.(time);
|
|
301
|
+
ui?.updateAnchors();
|
|
302
|
+
},
|
|
303
|
+
}, { step: opts.sim ? opts.sim.step : undefined, fixed, frames: host });
|
|
304
|
+
// ── XR ─────────────────────────────────────────────────────────────────
|
|
305
|
+
let xrSession = null;
|
|
306
|
+
let xrEntering = null;
|
|
307
|
+
// Restores the page host, pointers and surface; set while a session runs, idempotent.
|
|
308
|
+
let leaveXr = null;
|
|
309
|
+
const xr = {
|
|
310
|
+
get active() {
|
|
311
|
+
return xrSession !== null;
|
|
312
|
+
},
|
|
313
|
+
get session() {
|
|
314
|
+
return xrSession;
|
|
315
|
+
},
|
|
316
|
+
get surface() {
|
|
317
|
+
return xrSurface;
|
|
318
|
+
},
|
|
319
|
+
async supported() {
|
|
320
|
+
const sys = globalThis.navigator?.xr;
|
|
321
|
+
if (!sys || !XrSurface.supported())
|
|
322
|
+
return false;
|
|
323
|
+
try {
|
|
324
|
+
return await sys.isSessionSupported('immersive-vr');
|
|
325
|
+
}
|
|
326
|
+
catch {
|
|
327
|
+
return false;
|
|
328
|
+
}
|
|
329
|
+
},
|
|
330
|
+
enter() {
|
|
331
|
+
if (xrSession)
|
|
332
|
+
return Promise.resolve();
|
|
333
|
+
if (xrEntering)
|
|
334
|
+
return xrEntering;
|
|
335
|
+
const sys = globalThis.navigator?.xr;
|
|
336
|
+
if (!sys || !XrSurface.supported()) {
|
|
337
|
+
return Promise.reject(new Error('WebXR with the WebGPU binding is not available here'));
|
|
338
|
+
}
|
|
339
|
+
const refType = opts.xr?.referenceSpace ?? 'local-floor';
|
|
340
|
+
xrEntering = (async () => {
|
|
341
|
+
const session = await sys.requestSession('immersive-vr', {
|
|
342
|
+
requiredFeatures: ['webgpu', refType],
|
|
343
|
+
optionalFeatures: ['hand-tracking'],
|
|
344
|
+
});
|
|
345
|
+
let referenceSpace;
|
|
346
|
+
try {
|
|
347
|
+
referenceSpace = await session.requestReferenceSpace(refType);
|
|
348
|
+
}
|
|
349
|
+
catch (err) {
|
|
350
|
+
await session.end().catch(() => { });
|
|
351
|
+
throw err;
|
|
352
|
+
}
|
|
353
|
+
const surface = new XrSurface(session, gpu.device, {
|
|
354
|
+
referenceSpace,
|
|
355
|
+
scene: true,
|
|
356
|
+
colorFormat: renderer.format,
|
|
357
|
+
aux: opts.aux,
|
|
358
|
+
});
|
|
359
|
+
const sessionHost = createXrHost(session, host);
|
|
360
|
+
const source = new XrPointerSource(session, referenceSpace, () => sessionHost.frame);
|
|
361
|
+
const removeSource = pointers.addSource(source);
|
|
362
|
+
const onEnd = () => leaveXr?.();
|
|
363
|
+
leaveXr = () => {
|
|
364
|
+
leaveXr = null;
|
|
365
|
+
session.removeEventListener('end', onEnd);
|
|
366
|
+
removeSource();
|
|
367
|
+
loop.setFrames(host);
|
|
368
|
+
xrHost = null;
|
|
369
|
+
xrSurface = null;
|
|
370
|
+
xrSession = null;
|
|
371
|
+
surface.destroy();
|
|
372
|
+
opts.xr?.onExit?.();
|
|
373
|
+
};
|
|
374
|
+
session.addEventListener('end', onEnd);
|
|
375
|
+
xrSession = session;
|
|
376
|
+
xrSurface = surface;
|
|
377
|
+
xrHost = sessionHost;
|
|
378
|
+
loop.setFrames(sessionHost);
|
|
379
|
+
opts.xr?.onEnter?.(session);
|
|
380
|
+
})().finally(() => {
|
|
381
|
+
xrEntering = null;
|
|
382
|
+
});
|
|
383
|
+
return xrEntering;
|
|
384
|
+
},
|
|
385
|
+
async exit() {
|
|
386
|
+
const session = xrSession;
|
|
387
|
+
if (!session)
|
|
388
|
+
return;
|
|
389
|
+
try {
|
|
390
|
+
await session.end();
|
|
391
|
+
}
|
|
392
|
+
finally {
|
|
393
|
+
// The `end` event normally restored everything; a session ending without it does here.
|
|
394
|
+
leaveXr?.();
|
|
395
|
+
}
|
|
396
|
+
},
|
|
397
|
+
};
|
|
398
|
+
// Hidden, pagehide (iOS app switch) or freeze (Android): stop drawing and persist now; a
|
|
399
|
+
// phone may kill a background tab without another word.
|
|
400
|
+
if (dom) {
|
|
401
|
+
watchLifecycle({
|
|
402
|
+
onHide: (reason) => {
|
|
403
|
+
loop.stop();
|
|
404
|
+
hooks.hide?.(reason);
|
|
405
|
+
save?.flush();
|
|
406
|
+
},
|
|
407
|
+
onShow: (reason) => {
|
|
408
|
+
if (loop.halted)
|
|
409
|
+
return;
|
|
410
|
+
loop.start();
|
|
411
|
+
hooks.show?.(reason);
|
|
412
|
+
},
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
// ── Host lifecycle (ws/host-mobile) ────────────────────────────────────
|
|
416
|
+
// A host that knows the app's state (the web host from the page's visibility, a store app's
|
|
417
|
+
// from its shell): background stops frames and flushes the save, active resumes. On the web
|
|
418
|
+
// host the page events above have usually done both already (the loop is then stopped, so the
|
|
419
|
+
// flush is not repeated); a shell's state change without a page event does them here. Audio
|
|
420
|
+
// follows the document's visibility in `audio.attach`; a shell's background state also reaches
|
|
421
|
+
// it through `audio.background` (ws/audio-4), the same flag, so it is not suspended twice.
|
|
422
|
+
host.lifecycle?.watch((state) => {
|
|
423
|
+
audio.background(state === 'background');
|
|
424
|
+
if (state === 'background') {
|
|
425
|
+
if (loop.isRunning) {
|
|
426
|
+
loop.stop();
|
|
427
|
+
save?.flush();
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
else if (!loop.halted) {
|
|
431
|
+
loop.start();
|
|
432
|
+
}
|
|
433
|
+
hooks.lifecycle?.(state);
|
|
434
|
+
});
|
|
435
|
+
// Device lost: boot has shown the reload screen; stop all work behind it for good.
|
|
436
|
+
res.onDeviceLost((info) => {
|
|
437
|
+
loop.halt();
|
|
438
|
+
hooks.deviceLost?.(info);
|
|
439
|
+
void audio.halt();
|
|
440
|
+
ui?.caption.suspend(true);
|
|
441
|
+
save?.flush();
|
|
442
|
+
});
|
|
443
|
+
Object.assign(ctx, {
|
|
444
|
+
design,
|
|
445
|
+
surface,
|
|
446
|
+
gpu,
|
|
447
|
+
renderer,
|
|
448
|
+
camera: renderer.camera,
|
|
449
|
+
setCamera: (cam) => {
|
|
450
|
+
renderer.camera = cam;
|
|
451
|
+
},
|
|
452
|
+
post,
|
|
453
|
+
particles,
|
|
454
|
+
input,
|
|
455
|
+
clock,
|
|
456
|
+
replay,
|
|
457
|
+
gestures,
|
|
458
|
+
pointers,
|
|
459
|
+
padNav,
|
|
460
|
+
audio,
|
|
461
|
+
host,
|
|
462
|
+
save,
|
|
463
|
+
stack,
|
|
464
|
+
loop,
|
|
465
|
+
hooks,
|
|
466
|
+
onDeviceLost: res.onDeviceLost,
|
|
467
|
+
xr,
|
|
468
|
+
});
|
|
469
|
+
// `overlay: false`: `ctx.ui` throws instead of handing out an overlay that does not exist.
|
|
470
|
+
const liveUi = ui;
|
|
471
|
+
Object.defineProperty(ctx, 'ui', {
|
|
472
|
+
get: liveUi
|
|
473
|
+
? () => liveUi
|
|
474
|
+
: () => {
|
|
475
|
+
throw new Error(NO_DOM('ctx.ui'));
|
|
476
|
+
},
|
|
477
|
+
enumerable: liveUi !== null,
|
|
478
|
+
configurable: true,
|
|
479
|
+
});
|
|
480
|
+
// `camera` follows `setCamera` (`Object.assign` copied a value).
|
|
481
|
+
Object.defineProperty(ctx, 'camera', {
|
|
482
|
+
get: () => renderer.camera,
|
|
483
|
+
enumerable: true,
|
|
484
|
+
configurable: true,
|
|
485
|
+
});
|
|
486
|
+
return ctx;
|
|
487
|
+
}
|
|
488
|
+
/**
|
|
489
|
+
* `boot` for a headless game (`CreateGameOptions.gpu`): the surface is the one given (mounted
|
|
490
|
+
* as usual, or detached with `overlay: false`), the GPU is the one given, and device loss comes
|
|
491
|
+
* from `gpu.device.lost` when the device has one (a reason of `destroyed` is an explicit
|
|
492
|
+
* `destroy()`, not a loss).
|
|
493
|
+
*/
|
|
494
|
+
function gpuBoot(surface, gpu) {
|
|
495
|
+
const listeners = new Set();
|
|
496
|
+
const lost = gpu.device.lost;
|
|
497
|
+
if (lost && typeof lost.then === 'function') {
|
|
498
|
+
void lost.then((info) => {
|
|
499
|
+
if (info.reason === 'destroyed')
|
|
500
|
+
return;
|
|
501
|
+
for (const fn of [...listeners]) {
|
|
502
|
+
try {
|
|
503
|
+
fn(info);
|
|
504
|
+
}
|
|
505
|
+
catch (err) {
|
|
506
|
+
console.error('[createGame] device-lost handler failed', err);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
return {
|
|
512
|
+
ok: true,
|
|
513
|
+
surface,
|
|
514
|
+
gpu,
|
|
515
|
+
onDeviceLost(fn) {
|
|
516
|
+
listeners.add(fn);
|
|
517
|
+
return () => listeners.delete(fn);
|
|
518
|
+
},
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* `PadNav`'s stack with `overlay: false`: no panels, so the pad never leaves the game. PadNav
|
|
523
|
+
* reads only `padOwner` and `onPadOwnerChange`.
|
|
524
|
+
*/
|
|
525
|
+
const NO_PANELS = {
|
|
526
|
+
padOwner: 'game',
|
|
527
|
+
onPadOwnerChange: () => () => { },
|
|
528
|
+
};
|
|
529
|
+
/** What the XR render step iterates when the frame has no viewer pose. */
|
|
530
|
+
const NO_EYES = [];
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Positional hashing (ws/core-3): a stable random value per lattice cell, for rolls that need a
|
|
3
|
+
* number per point rather than a field between points (a tree in this column, a speckle on this
|
|
4
|
+
* texel, a lantern on this floor). Noise is `createNoise`; a sequence is `createRandom`.
|
|
5
|
+
*
|
|
6
|
+
* The seed goes through `seedKey`, the same hash `createRandom` uses (an integer number seed is
|
|
7
|
+
* taken `>>> 0`, anything else is FNV-1a over UTF-8). Each coordinate is truncated toward zero
|
|
8
|
+
* and wrapped to 32 bits (`x | 0`), so `hash2(s, 2.9, -0.5)` is `hash2(s, 2, 0)`: floor first
|
|
9
|
+
* when a cell is `Math.floor(x)`. The key, tagged per dimension, and then each coordinate in turn
|
|
10
|
+
* are folded in with Wellons' lowbias32 finaliser (xor-shift-multiply, a bijection on 32 bits),
|
|
11
|
+
* so neighbouring cells share no bits and `hash3(s, x, 0, z)` is unrelated to `hash2(s, x, z)`.
|
|
12
|
+
*
|
|
13
|
+
* Pure integer arithmetic (`Math.imul`, shifts): the same uint32 on every engine. Nothing
|
|
14
|
+
* allocates; a string seed is rehashed per call, so a hot loop passes a number seed.
|
|
15
|
+
*/
|
|
16
|
+
import { type Seed } from './random.js';
|
|
17
|
+
/** A uint32 for cell `(x, z)` under `seed`. */
|
|
18
|
+
export declare function hash2(seed: Seed, x: number, z: number): number;
|
|
19
|
+
/** A uint32 for cell `(x, y, z)` under `seed`. */
|
|
20
|
+
export declare function hash3(seed: Seed, x: number, y: number, z: number): number;
|
|
21
|
+
/** `hash2` as a float in [0, 1). */
|
|
22
|
+
export declare function hash2f(seed: Seed, x: number, z: number): number;
|
|
23
|
+
/** `hash3` as a float in [0, 1). */
|
|
24
|
+
export declare function hash3f(seed: Seed, x: number, y: number, z: number): number;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Positional hashing (ws/core-3): a stable random value per lattice cell, for rolls that need a
|
|
3
|
+
* number per point rather than a field between points (a tree in this column, a speckle on this
|
|
4
|
+
* texel, a lantern on this floor). Noise is `createNoise`; a sequence is `createRandom`.
|
|
5
|
+
*
|
|
6
|
+
* The seed goes through `seedKey`, the same hash `createRandom` uses (an integer number seed is
|
|
7
|
+
* taken `>>> 0`, anything else is FNV-1a over UTF-8). Each coordinate is truncated toward zero
|
|
8
|
+
* and wrapped to 32 bits (`x | 0`), so `hash2(s, 2.9, -0.5)` is `hash2(s, 2, 0)`: floor first
|
|
9
|
+
* when a cell is `Math.floor(x)`. The key, tagged per dimension, and then each coordinate in turn
|
|
10
|
+
* are folded in with Wellons' lowbias32 finaliser (xor-shift-multiply, a bijection on 32 bits),
|
|
11
|
+
* so neighbouring cells share no bits and `hash3(s, x, 0, z)` is unrelated to `hash2(s, x, z)`.
|
|
12
|
+
*
|
|
13
|
+
* Pure integer arithmetic (`Math.imul`, shifts): the same uint32 on every engine. Nothing
|
|
14
|
+
* allocates; a string seed is rehashed per call, so a hot loop passes a number seed.
|
|
15
|
+
*/
|
|
16
|
+
import { seedKey } from './random.js';
|
|
17
|
+
const INV_2_32 = 1 / 4294967296;
|
|
18
|
+
/** Per-dimension tags folded into the key first, so 2D and 3D hashes of one seed differ. */
|
|
19
|
+
const TAG2 = 0x6a09e667;
|
|
20
|
+
const TAG3 = 0xbb67ae85;
|
|
21
|
+
/** lowbias32 (Chris Wellons, hash-prospector): a well-mixed bijection on uint32. */
|
|
22
|
+
function mix(h) {
|
|
23
|
+
h ^= h >>> 16;
|
|
24
|
+
h = Math.imul(h, 0x7feb352d);
|
|
25
|
+
h ^= h >>> 15;
|
|
26
|
+
h = Math.imul(h, 0x846ca68b);
|
|
27
|
+
return (h ^ (h >>> 16)) >>> 0;
|
|
28
|
+
}
|
|
29
|
+
/** A uint32 for cell `(x, z)` under `seed`. */
|
|
30
|
+
export function hash2(seed, x, z) {
|
|
31
|
+
let h = mix(seedKey(seed) ^ TAG2);
|
|
32
|
+
h = mix(h ^ (x | 0));
|
|
33
|
+
return mix(h ^ (z | 0));
|
|
34
|
+
}
|
|
35
|
+
/** A uint32 for cell `(x, y, z)` under `seed`. */
|
|
36
|
+
export function hash3(seed, x, y, z) {
|
|
37
|
+
let h = mix(seedKey(seed) ^ TAG3);
|
|
38
|
+
h = mix(h ^ (x | 0));
|
|
39
|
+
h = mix(h ^ (y | 0));
|
|
40
|
+
return mix(h ^ (z | 0));
|
|
41
|
+
}
|
|
42
|
+
/** `hash2` as a float in [0, 1). */
|
|
43
|
+
export function hash2f(seed, x, z) {
|
|
44
|
+
return hash2(seed, x, z) * INV_2_32;
|
|
45
|
+
}
|
|
46
|
+
/** `hash3` as a float in [0, 1). */
|
|
47
|
+
export function hash3f(seed, x, y, z) {
|
|
48
|
+
return hash3(seed, x, y, z) * INV_2_32;
|
|
49
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type { EventMap } from './events.js';
|
|
2
|
+
export { Emitter } from './events.js';
|
|
3
|
+
export { hash2, hash2f, hash3, hash3f } from './hash.js';
|
|
4
|
+
export type { FixedStepperOptions, GameLoopOptions, LoopCallbacks, Time } from './loop.js';
|
|
5
|
+
export { FixedStepper, GameLoop } from './loop.js';
|
|
6
|
+
export type { Mat4 } from './math.js';
|
|
7
|
+
export { mat4 } from './math.js';
|
|
8
|
+
export type { FbmOptions, Noise, NoiseFn } from './noise.js';
|
|
9
|
+
export { createNoise } from './noise.js';
|
|
10
|
+
export type { Pool, PoolOptions } from './pool.js';
|
|
11
|
+
export { createPool } from './pool.js';
|
|
12
|
+
export type { NumberSink, Random, Seed } from './random.js';
|
|
13
|
+
export { createRandom, seedKey } from './random.js';
|
|
14
|
+
export type { Scene, TransitionListener, TransitionPhase } from './scene.js';
|
|
15
|
+
export { SceneStack } from './scene.js';
|
|
16
|
+
export type { Cooldown, EveryOptions, Scheduler, TimerHandle } from './timer.js';
|
|
17
|
+
export { createScheduler } from './timer.js';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { Emitter } from './events.js';
|
|
2
|
+
export { hash2, hash2f, hash3, hash3f } from './hash.js';
|
|
3
|
+
export { FixedStepper, GameLoop } from './loop.js';
|
|
4
|
+
export { mat4 } from './math.js';
|
|
5
|
+
export { createNoise } from './noise.js';
|
|
6
|
+
export { createPool } from './pool.js';
|
|
7
|
+
export { createRandom, seedKey } from './random.js';
|
|
8
|
+
export { SceneStack } from './scene.js';
|
|
9
|
+
export { createScheduler } from './timer.js';
|