@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,146 @@
|
|
|
1
|
+
import { focusableWithin } from './dom.js';
|
|
2
|
+
import { activateLink } from './link.js';
|
|
3
|
+
// Standard mapping: 0 = A/south, 1 = B/east, 12–15 = d-pad up, down, left, right.
|
|
4
|
+
const BTN_ACTIVATE = 0;
|
|
5
|
+
const BTN_BACK = 1;
|
|
6
|
+
const DPAD = { up: 12, down: 13, left: 14, right: 15 };
|
|
7
|
+
const DIRS = ['up', 'down', 'left', 'right'];
|
|
8
|
+
/** `navigator.getGamepads()`, guarded; empty where there is no navigator. */
|
|
9
|
+
export function defaultPads() {
|
|
10
|
+
try {
|
|
11
|
+
return typeof navigator !== 'undefined' && navigator.getGamepads ? navigator.getGamepads() : [];
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return [];
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Drives the top modal panel with a gamepad, the way Tab, Enter and Esc drive it from a
|
|
19
|
+
* keyboard: the d-pad (or left stick) moves focus through the panel's controls, A activates
|
|
20
|
+
* the focused one, B dismisses the panel. On a focused range slider left/right step its value,
|
|
21
|
+
* and on a `select` they pick the previous/next option. Call `update()` once per frame. It does
|
|
22
|
+
* nothing (and reads no pad) while the stack does not own the pad (`stack.padOwner`), so the
|
|
23
|
+
* game's own input map keeps it; the latch resets on each owner transition.
|
|
24
|
+
*/
|
|
25
|
+
export class PadNav {
|
|
26
|
+
stack;
|
|
27
|
+
getGamepads;
|
|
28
|
+
threshold;
|
|
29
|
+
prev = new Set();
|
|
30
|
+
next = new Set();
|
|
31
|
+
/** True while the stack owns the pad (a modal is open); kept by `onPadOwnerChange`. */
|
|
32
|
+
owning;
|
|
33
|
+
unwatch;
|
|
34
|
+
constructor(stack, opts = {}) {
|
|
35
|
+
this.stack = stack;
|
|
36
|
+
this.getGamepads = opts.getGamepads ?? defaultPads;
|
|
37
|
+
this.threshold = opts.axisThreshold ?? 0.6;
|
|
38
|
+
this.owning = stack.padOwner === 'stack';
|
|
39
|
+
// The latch resets on each transition: what is held when the panel opens is not a fresh
|
|
40
|
+
// press on it (the A that opened a menu never activates its first control), and nothing is
|
|
41
|
+
// read while the game owns the pad.
|
|
42
|
+
this.unwatch = stack.onPadOwnerChange((owner) => {
|
|
43
|
+
this.owning = owner === 'stack';
|
|
44
|
+
this.prev.clear();
|
|
45
|
+
if (this.owning)
|
|
46
|
+
readPad(this.getGamepads(), this.threshold, this.prev);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
/** Stop following the stack; `update()` does nothing after. */
|
|
50
|
+
dispose() {
|
|
51
|
+
this.unwatch();
|
|
52
|
+
this.owning = false;
|
|
53
|
+
}
|
|
54
|
+
update() {
|
|
55
|
+
if (!this.owning)
|
|
56
|
+
return;
|
|
57
|
+
const now = readPad(this.getGamepads(), this.threshold, this.next);
|
|
58
|
+
const before = this.prev;
|
|
59
|
+
const fresh = (k) => now.has(k) && !before.has(k);
|
|
60
|
+
this.next = before;
|
|
61
|
+
this.prev = now;
|
|
62
|
+
const panel = this.stack.topModalElement;
|
|
63
|
+
if (!panel)
|
|
64
|
+
return;
|
|
65
|
+
if (fresh('back')) {
|
|
66
|
+
this.stack.dismissTop();
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const active = panel.ownerDocument.activeElement;
|
|
70
|
+
const inside = active !== null && active !== panel && panel.contains(active);
|
|
71
|
+
if (fresh('activate')) {
|
|
72
|
+
// External links open through the host with block detection (a pad press is no gesture).
|
|
73
|
+
if (inside)
|
|
74
|
+
activateLink(active, 'gamepad');
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
for (const dir of ['up', 'down', 'left', 'right']) {
|
|
78
|
+
if (!fresh(dir))
|
|
79
|
+
continue;
|
|
80
|
+
if (inside && (dir === 'left' || dir === 'right') && this.adjust(active, dir === 'right'))
|
|
81
|
+
return;
|
|
82
|
+
const items = focusableWithin(panel);
|
|
83
|
+
if (items.length === 0)
|
|
84
|
+
return;
|
|
85
|
+
const i = inside ? items.indexOf(active) : -1;
|
|
86
|
+
const step = dir === 'down' || dir === 'right' ? 1 : -1;
|
|
87
|
+
const next = i < 0 ? (step > 0 ? 0 : items.length - 1) : (i + step + items.length) % items.length;
|
|
88
|
+
items[next]?.focus();
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/** Left/right on a value control changes the value instead of moving focus. */
|
|
93
|
+
adjust(el, up) {
|
|
94
|
+
if (el instanceof HTMLInputElement && el.type === 'range') {
|
|
95
|
+
const num = (v, d) => (v === '' || Number.isNaN(Number(v)) ? d : Number(v));
|
|
96
|
+
const min = num(el.min, 0);
|
|
97
|
+
const max = num(el.max, 100);
|
|
98
|
+
const step = el.step === 'any' ? (max - min) / 20 : num(el.step, 1);
|
|
99
|
+
const v = Math.min(max, Math.max(min, Number(el.value) + (up ? step : -step)));
|
|
100
|
+
el.value = String(Math.round(v / step) * step);
|
|
101
|
+
}
|
|
102
|
+
else if (el instanceof HTMLSelectElement) {
|
|
103
|
+
const n = el.options.length;
|
|
104
|
+
if (n === 0)
|
|
105
|
+
return true;
|
|
106
|
+
el.selectedIndex = Math.max(0, Math.min(n - 1, el.selectedIndex + (up ? 1 : -1)));
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
112
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* The controls held on any of `pads` right now (standard mapping: 0 = A, 1 = B, 12–15 = d-pad;
|
|
118
|
+
* the left stick past `threshold` counts as the d-pad). `out` is cleared and filled when given,
|
|
119
|
+
* so a per-frame reader allocates nothing. `PadNav` and the world cursor (`ui/cursor.ts`) latch
|
|
120
|
+
* on this set: a control is fresh when it is in this frame's set and not the last one's.
|
|
121
|
+
*/
|
|
122
|
+
export function readPad(pads, threshold = 0.6, out = new Set()) {
|
|
123
|
+
out.clear();
|
|
124
|
+
for (const p of pads) {
|
|
125
|
+
if (!p)
|
|
126
|
+
continue;
|
|
127
|
+
const b = (i) => p.buttons[i]?.pressed === true;
|
|
128
|
+
if (b(BTN_ACTIVATE))
|
|
129
|
+
out.add('activate');
|
|
130
|
+
if (b(BTN_BACK))
|
|
131
|
+
out.add('back');
|
|
132
|
+
for (const dir of DIRS)
|
|
133
|
+
if (b(DPAD[dir]))
|
|
134
|
+
out.add(dir);
|
|
135
|
+
const [x = 0, y = 0] = p.axes ?? [];
|
|
136
|
+
if (x <= -threshold)
|
|
137
|
+
out.add('left');
|
|
138
|
+
if (x >= threshold)
|
|
139
|
+
out.add('right');
|
|
140
|
+
if (y <= -threshold)
|
|
141
|
+
out.add('up');
|
|
142
|
+
if (y >= threshold)
|
|
143
|
+
out.add('down');
|
|
144
|
+
}
|
|
145
|
+
return out;
|
|
146
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frame-cost readout (ws/perf-overlay): a monitor that keeps the last N frame times and copies
|
|
3
|
+
* the renderer's counters into one plain object, and a small DOM overlay that shows those
|
|
4
|
+
* numbers with a sparkline. Quality validation needs to see the cost on screen; demos, the
|
|
5
|
+
* bench and e2e read the same numbers without the DOM.
|
|
6
|
+
*
|
|
7
|
+
* ```ts
|
|
8
|
+
* const perf = createPerfMonitor();
|
|
9
|
+
* const hud = createPerfOverlay(perf, { parent: stageEl, corner: 'tr' });
|
|
10
|
+
* // each frame, after renderer.endFrame():
|
|
11
|
+
* state.perf = perf.sample(renderer);
|
|
12
|
+
* hud.update();
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* The monitor is pure (no DOM, no GPU): `begin`/`end` bracket the CPU frame when the caller
|
|
16
|
+
* wants an exact span, otherwise `sample` measures the interval between its own calls. Steady
|
|
17
|
+
* state allocates nothing: the ring, the stats object and the count and pass records are reused;
|
|
18
|
+
* a new counter or pass label adds a key once.
|
|
19
|
+
*/
|
|
20
|
+
/** What `PerfMonitor.sample` reads: the renderer's counters, its timing and its scale. */
|
|
21
|
+
export interface PerfSource {
|
|
22
|
+
/**
|
|
23
|
+
* `Renderer.stats` (any object; `RenderStats` has no index signature): every own numeric
|
|
24
|
+
* field is a counter (`drawCalls`, `instances`, `culled`, …).
|
|
25
|
+
*/
|
|
26
|
+
readonly stats: object;
|
|
27
|
+
/** `Renderer.timing`: the last GPU sample, by pass when the device has `timestamp-query`. */
|
|
28
|
+
readonly timing?: {
|
|
29
|
+
readonly gpuMs: number;
|
|
30
|
+
readonly passes: Readonly<Record<string, number>>;
|
|
31
|
+
readonly samples: number;
|
|
32
|
+
};
|
|
33
|
+
/** `Renderer.timingEvery`: how often the renderer samples GPU time (0 = never). */
|
|
34
|
+
timingEvery?: number;
|
|
35
|
+
/** `Renderer.resolutionScale`: effective scene-target scale vs the output. */
|
|
36
|
+
readonly resolutionScale?: number;
|
|
37
|
+
}
|
|
38
|
+
export interface PerfFrameStats {
|
|
39
|
+
/** Last frame time (ms). */
|
|
40
|
+
last: number;
|
|
41
|
+
/** Mean over the filled part of the ring (ms). */
|
|
42
|
+
avg: number;
|
|
43
|
+
/** 95th percentile over the filled part of the ring (ms). */
|
|
44
|
+
p95: number;
|
|
45
|
+
/** Largest sample in the ring (ms). */
|
|
46
|
+
max: number;
|
|
47
|
+
/** `1000 / avg`, 0 while nothing has been sampled. */
|
|
48
|
+
fps: number;
|
|
49
|
+
}
|
|
50
|
+
export interface PerfGpuStats {
|
|
51
|
+
/** GPU time of the renderer's last sample (ms). */
|
|
52
|
+
total: number;
|
|
53
|
+
/** Per pass label, ms; empty when the device has no `timestamp-query`. */
|
|
54
|
+
passes: Record<string, number>;
|
|
55
|
+
}
|
|
56
|
+
/** The numbers, as one plain JSON-safe object (the monitor reuses it; copy to keep one). */
|
|
57
|
+
export interface PerfSample {
|
|
58
|
+
frame: PerfFrameStats;
|
|
59
|
+
/** `null` until the renderer has delivered a GPU time sample. */
|
|
60
|
+
gpu: PerfGpuStats | null;
|
|
61
|
+
/**
|
|
62
|
+
* Every numeric field of `source.stats`, read by key each frame (never a fixed set), plus
|
|
63
|
+
* `draws` as an alias of `drawCalls` when the source has that counter.
|
|
64
|
+
*/
|
|
65
|
+
counts: Record<string, number>;
|
|
66
|
+
resolutionScale: number;
|
|
67
|
+
/** `performance.memory.usedJSHeapSize` (bytes) where the browser exposes it; else absent. */
|
|
68
|
+
memory?: number;
|
|
69
|
+
/** Frames sampled since creation or `reset()`. */
|
|
70
|
+
samples: number;
|
|
71
|
+
/** How many slots of `history` hold a frame time (`min(samples, window)`). */
|
|
72
|
+
filled: number;
|
|
73
|
+
/** The ring's length (frames). */
|
|
74
|
+
window: number;
|
|
75
|
+
}
|
|
76
|
+
export interface PerfMonitorOptions {
|
|
77
|
+
/** Frames kept in `history` and used for `avg`, `p95` and `max`. Default 120. */
|
|
78
|
+
window?: number;
|
|
79
|
+
/**
|
|
80
|
+
* When `sample` finds the source's `timingEvery` at 0, it sets it to this so GPU times arrive.
|
|
81
|
+
* Default 1 (the renderer keeps one sample in flight at a time anyway). 0 leaves it alone.
|
|
82
|
+
*/
|
|
83
|
+
gpuEvery?: number;
|
|
84
|
+
/** Clock (ms). Default `performance.now`. */
|
|
85
|
+
now?: () => number;
|
|
86
|
+
}
|
|
87
|
+
export interface PerfMonitor extends PerfSample {
|
|
88
|
+
/** Mark the start of the CPU frame. Optional: without it `sample` times the frame interval. */
|
|
89
|
+
begin(nowMs?: number): void;
|
|
90
|
+
/** Mark the end of the CPU frame; the span since `begin` is the next frame time. */
|
|
91
|
+
end(nowMs?: number): void;
|
|
92
|
+
/**
|
|
93
|
+
* Read the source once per frame, after `renderer.endFrame()`. Pushes the frame time into
|
|
94
|
+
* `history` and refreshes every field. Returns this monitor's `PerfSample` (the same object).
|
|
95
|
+
*/
|
|
96
|
+
sample(source: PerfSource): PerfSample;
|
|
97
|
+
/** Frame times (ms), a ring of `window` slots; `filled` says how many hold data. */
|
|
98
|
+
readonly history: Float32Array;
|
|
99
|
+
/** Index of the slot the next frame time goes into (the oldest sample while the ring is full). */
|
|
100
|
+
readonly head: number;
|
|
101
|
+
/** Forget every sample. */
|
|
102
|
+
reset(): void;
|
|
103
|
+
}
|
|
104
|
+
export declare function createPerfMonitor(opts?: PerfMonitorOptions): PerfMonitor;
|
|
105
|
+
export type PerfCorner = 'tl' | 'tr' | 'bl' | 'br';
|
|
106
|
+
export interface PerfOverlayOptions {
|
|
107
|
+
/** Which corner of the parent. Default `'tl'`. */
|
|
108
|
+
corner?: PerfCorner;
|
|
109
|
+
/** Draw the frame-time sparkline. Default true. */
|
|
110
|
+
graph?: boolean;
|
|
111
|
+
/**
|
|
112
|
+
* Element to mount into (positioned: the overlay is `position: absolute` inside it). Default:
|
|
113
|
+
* `document.body`, where the overlay is `position: fixed`.
|
|
114
|
+
*/
|
|
115
|
+
parent?: HTMLElement;
|
|
116
|
+
/** Redraw the numbers every N `update()` calls. Default 10. The graph redraws with them. */
|
|
117
|
+
updateEvery?: number;
|
|
118
|
+
}
|
|
119
|
+
export interface PerfOverlay {
|
|
120
|
+
/** The overlay root (`.tc-perf`), `aria-hidden`, `pointer-events: none`. */
|
|
121
|
+
readonly el: HTMLElement;
|
|
122
|
+
/** Call once a frame after `monitor.sample`; redraws every `updateEvery` calls. */
|
|
123
|
+
update(): void;
|
|
124
|
+
show(): void;
|
|
125
|
+
hide(): void;
|
|
126
|
+
toggle(): void;
|
|
127
|
+
readonly visible: boolean;
|
|
128
|
+
destroy(): void;
|
|
129
|
+
}
|
|
130
|
+
export declare function createPerfOverlay(monitor: PerfMonitor, opts?: PerfOverlayOptions): PerfOverlay;
|
package/dist/ui/perf.js
ADDED
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frame-cost readout (ws/perf-overlay): a monitor that keeps the last N frame times and copies
|
|
3
|
+
* the renderer's counters into one plain object, and a small DOM overlay that shows those
|
|
4
|
+
* numbers with a sparkline. Quality validation needs to see the cost on screen; demos, the
|
|
5
|
+
* bench and e2e read the same numbers without the DOM.
|
|
6
|
+
*
|
|
7
|
+
* ```ts
|
|
8
|
+
* const perf = createPerfMonitor();
|
|
9
|
+
* const hud = createPerfOverlay(perf, { parent: stageEl, corner: 'tr' });
|
|
10
|
+
* // each frame, after renderer.endFrame():
|
|
11
|
+
* state.perf = perf.sample(renderer);
|
|
12
|
+
* hud.update();
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* The monitor is pure (no DOM, no GPU): `begin`/`end` bracket the CPU frame when the caller
|
|
16
|
+
* wants an exact span, otherwise `sample` measures the interval between its own calls. Steady
|
|
17
|
+
* state allocates nothing: the ring, the stats object and the count and pass records are reused;
|
|
18
|
+
* a new counter or pass label adds a key once.
|
|
19
|
+
*/
|
|
20
|
+
import { h } from './dom.js';
|
|
21
|
+
const DEFAULT_WINDOW = 120;
|
|
22
|
+
/** `performance.memory.usedJSHeapSize` where it exists (Chromium); never throws. */
|
|
23
|
+
function usedHeap() {
|
|
24
|
+
try {
|
|
25
|
+
const mem = globalThis.performance?.memory;
|
|
26
|
+
const used = mem?.usedJSHeapSize;
|
|
27
|
+
return typeof used === 'number' && Number.isFinite(used) ? used : undefined;
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export function createPerfMonitor(opts = {}) {
|
|
34
|
+
const window = Math.max(1, Math.floor(opts.window ?? DEFAULT_WINDOW));
|
|
35
|
+
const gpuEvery = opts.gpuEvery ?? 1;
|
|
36
|
+
const now = opts.now ?? (() => performance.now());
|
|
37
|
+
const history = new Float32Array(window);
|
|
38
|
+
const sorted = new Float32Array(window);
|
|
39
|
+
let head = 0;
|
|
40
|
+
let filled = 0;
|
|
41
|
+
let samples = 0;
|
|
42
|
+
/** `begin` time, or NaN when `begin` was not called since the last sample. */
|
|
43
|
+
let beginAt = Number.NaN;
|
|
44
|
+
/** Frame time from `end`, or NaN when `end` was not called since the last sample. */
|
|
45
|
+
let ended = Number.NaN;
|
|
46
|
+
/** The clock at the previous `sample`, for the interval fallback. */
|
|
47
|
+
let lastSampleAt = Number.NaN;
|
|
48
|
+
const frame = { last: 0, avg: 0, p95: 0, max: 0, fps: 0 };
|
|
49
|
+
const gpu = { total: 0, passes: {} };
|
|
50
|
+
const counts = {};
|
|
51
|
+
const monitor = {
|
|
52
|
+
frame,
|
|
53
|
+
gpu: null,
|
|
54
|
+
counts,
|
|
55
|
+
resolutionScale: 1,
|
|
56
|
+
memory: undefined,
|
|
57
|
+
samples: 0,
|
|
58
|
+
filled: 0,
|
|
59
|
+
window,
|
|
60
|
+
history,
|
|
61
|
+
get head() {
|
|
62
|
+
return head;
|
|
63
|
+
},
|
|
64
|
+
begin(nowMs = now()) {
|
|
65
|
+
beginAt = nowMs;
|
|
66
|
+
},
|
|
67
|
+
end(nowMs = now()) {
|
|
68
|
+
if (Number.isNaN(beginAt))
|
|
69
|
+
return;
|
|
70
|
+
ended = Math.max(0, nowMs - beginAt);
|
|
71
|
+
beginAt = Number.NaN;
|
|
72
|
+
},
|
|
73
|
+
sample(source) {
|
|
74
|
+
const t = now();
|
|
75
|
+
let ms;
|
|
76
|
+
if (!Number.isNaN(ended)) {
|
|
77
|
+
ms = ended;
|
|
78
|
+
ended = Number.NaN;
|
|
79
|
+
}
|
|
80
|
+
else if (!Number.isNaN(beginAt)) {
|
|
81
|
+
// `begin` without `end`: the span up to this call.
|
|
82
|
+
ms = Math.max(0, t - beginAt);
|
|
83
|
+
beginAt = Number.NaN;
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
ms = Number.isNaN(lastSampleAt) ? 0 : Math.max(0, t - lastSampleAt);
|
|
87
|
+
}
|
|
88
|
+
lastSampleAt = t;
|
|
89
|
+
history[head] = ms;
|
|
90
|
+
head = (head + 1) % window;
|
|
91
|
+
if (filled < window)
|
|
92
|
+
filled++;
|
|
93
|
+
samples++;
|
|
94
|
+
monitor.samples = samples;
|
|
95
|
+
monitor.filled = filled;
|
|
96
|
+
// Frame statistics over the filled part of the ring.
|
|
97
|
+
let sum = 0;
|
|
98
|
+
let max = 0;
|
|
99
|
+
for (let i = 0; i < filled; i++) {
|
|
100
|
+
const v = history[i];
|
|
101
|
+
sum += v;
|
|
102
|
+
if (v > max)
|
|
103
|
+
max = v;
|
|
104
|
+
sorted[i] = v;
|
|
105
|
+
}
|
|
106
|
+
// Unfilled slots sort last, so no subarray view is made per frame.
|
|
107
|
+
for (let i = filled; i < window; i++)
|
|
108
|
+
sorted[i] = Number.POSITIVE_INFINITY;
|
|
109
|
+
sorted.sort();
|
|
110
|
+
frame.last = ms;
|
|
111
|
+
frame.avg = sum / filled;
|
|
112
|
+
frame.max = max;
|
|
113
|
+
frame.p95 = sorted[Math.min(filled - 1, Math.floor(0.95 * filled))];
|
|
114
|
+
frame.fps = frame.avg > 0 ? 1000 / frame.avg : 0;
|
|
115
|
+
// Counters, by key.
|
|
116
|
+
const stats = source.stats;
|
|
117
|
+
for (const k in stats) {
|
|
118
|
+
const v = stats[k];
|
|
119
|
+
if (typeof v === 'number')
|
|
120
|
+
counts[k] = v;
|
|
121
|
+
}
|
|
122
|
+
if (typeof stats.drawCalls === 'number' && typeof stats.draws !== 'number') {
|
|
123
|
+
counts.draws = stats.drawCalls;
|
|
124
|
+
}
|
|
125
|
+
// GPU time, once the renderer samples it.
|
|
126
|
+
const timing = source.timing;
|
|
127
|
+
if (timing && gpuEvery > 0 && source.timingEvery === 0)
|
|
128
|
+
source.timingEvery = gpuEvery;
|
|
129
|
+
if (timing && timing.samples > 0) {
|
|
130
|
+
gpu.total = timing.gpuMs;
|
|
131
|
+
const passes = timing.passes;
|
|
132
|
+
for (const k in passes) {
|
|
133
|
+
const v = passes[k];
|
|
134
|
+
if (typeof v === 'number')
|
|
135
|
+
gpu.passes[k] = v;
|
|
136
|
+
}
|
|
137
|
+
monitor.gpu = gpu;
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
monitor.gpu = null;
|
|
141
|
+
}
|
|
142
|
+
const scale = source.resolutionScale;
|
|
143
|
+
monitor.resolutionScale = typeof scale === 'number' && Number.isFinite(scale) ? scale : 1;
|
|
144
|
+
monitor.memory = usedHeap();
|
|
145
|
+
return monitor;
|
|
146
|
+
},
|
|
147
|
+
reset() {
|
|
148
|
+
history.fill(0);
|
|
149
|
+
head = 0;
|
|
150
|
+
filled = 0;
|
|
151
|
+
samples = 0;
|
|
152
|
+
beginAt = Number.NaN;
|
|
153
|
+
ended = Number.NaN;
|
|
154
|
+
lastSampleAt = Number.NaN;
|
|
155
|
+
frame.last = frame.avg = frame.p95 = frame.max = frame.fps = 0;
|
|
156
|
+
gpu.total = 0;
|
|
157
|
+
for (const k in gpu.passes)
|
|
158
|
+
delete gpu.passes[k];
|
|
159
|
+
for (const k in counts)
|
|
160
|
+
delete counts[k];
|
|
161
|
+
monitor.gpu = null;
|
|
162
|
+
monitor.samples = 0;
|
|
163
|
+
monitor.filled = 0;
|
|
164
|
+
monitor.resolutionScale = 1;
|
|
165
|
+
monitor.memory = undefined;
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
return monitor;
|
|
169
|
+
}
|
|
170
|
+
const STYLE_ATTR = 'perf';
|
|
171
|
+
/**
|
|
172
|
+
* Theme tokens with fallbacks (`defaultTheme`'s values), so the overlay reads the same on a
|
|
173
|
+
* page without a `.tc-overlay` root. Sizes are CSS px: this is a debug readout, not game UI.
|
|
174
|
+
*/
|
|
175
|
+
const CSS = /* css */ `
|
|
176
|
+
.tc-perf { position: absolute; z-index: 1000; margin: 8px; padding: 6px 8px; min-width: 180px;
|
|
177
|
+
pointer-events: none; user-select: none; -webkit-user-select: none; box-sizing: border-box;
|
|
178
|
+
font-family: var(--tc-font-mono, ui-monospace, Menlo, Consolas, monospace); font-size: 11px; line-height: 1.35;
|
|
179
|
+
color: var(--tc-color-text, #ece6d8); background: var(--tc-color-surface, rgba(18, 21, 30, 0.94));
|
|
180
|
+
border: 1px solid var(--tc-color-border, rgba(224, 180, 90, 0.35)); border-radius: var(--tc-radius-s, 6px);
|
|
181
|
+
white-space: pre; font-variant-numeric: tabular-nums; }
|
|
182
|
+
.tc-perf[hidden] { display: none; }
|
|
183
|
+
.tc-perf[data-fixed] { position: fixed; }
|
|
184
|
+
.tc-perf[data-corner^="t"] { top: 0; }
|
|
185
|
+
.tc-perf[data-corner^="b"] { bottom: 0; }
|
|
186
|
+
.tc-perf[data-corner$="l"] { left: 0; }
|
|
187
|
+
.tc-perf[data-corner$="r"] { right: 0; }
|
|
188
|
+
.tc-perf-head { color: var(--tc-color-accent, #e0b45a); }
|
|
189
|
+
.tc-perf-dim { color: var(--tc-color-text-muted, #a7a091); }
|
|
190
|
+
.tc-perf-graph { display: block; width: 100%; height: 36px; margin-top: 4px; }
|
|
191
|
+
`;
|
|
192
|
+
const GRAPH_W = 240;
|
|
193
|
+
const GRAPH_H = 36;
|
|
194
|
+
/** The graph's floor: two 60 Hz frames, so a steady 16.7 ms sits at half height. */
|
|
195
|
+
const GRAPH_MIN_MS = 1000 / 30;
|
|
196
|
+
function fmt(ms) {
|
|
197
|
+
return ms >= 100 ? ms.toFixed(0) : ms.toFixed(ms >= 10 ? 1 : 2);
|
|
198
|
+
}
|
|
199
|
+
function fmtBytes(b) {
|
|
200
|
+
if (b >= 1024 * 1024 * 1024)
|
|
201
|
+
return `${(b / (1024 * 1024 * 1024)).toFixed(2)} GB`;
|
|
202
|
+
if (b >= 1024 * 1024)
|
|
203
|
+
return `${(b / (1024 * 1024)).toFixed(1)} MB`;
|
|
204
|
+
return `${(b / 1024).toFixed(0)} KB`;
|
|
205
|
+
}
|
|
206
|
+
export function createPerfOverlay(monitor, opts = {}) {
|
|
207
|
+
const parent = opts.parent ?? document.body;
|
|
208
|
+
const doc = parent.ownerDocument;
|
|
209
|
+
const updateEvery = Math.max(1, Math.floor(opts.updateEvery ?? 10));
|
|
210
|
+
const graph = opts.graph ?? true;
|
|
211
|
+
if (!doc.querySelector(`style[data-tc="${STYLE_ATTR}"]`)) {
|
|
212
|
+
doc.head.append(h('style', { 'data-tc': STYLE_ATTR }, CSS));
|
|
213
|
+
}
|
|
214
|
+
const head = h('div', { class: 'tc-perf-head' });
|
|
215
|
+
const body = h('div', { class: 'tc-perf-body' });
|
|
216
|
+
const canvas = graph
|
|
217
|
+
? h('canvas', { class: 'tc-perf-graph', width: GRAPH_W, height: GRAPH_H })
|
|
218
|
+
: null;
|
|
219
|
+
const el = h('div', {
|
|
220
|
+
class: 'tc-perf',
|
|
221
|
+
'aria-hidden': 'true',
|
|
222
|
+
dataset: { corner: opts.corner ?? 'tl' },
|
|
223
|
+
'data-fixed': !opts.parent,
|
|
224
|
+
}, head, body, canvas);
|
|
225
|
+
parent.append(el);
|
|
226
|
+
let ctx = null;
|
|
227
|
+
try {
|
|
228
|
+
ctx = canvas?.getContext('2d') ?? null;
|
|
229
|
+
}
|
|
230
|
+
catch {
|
|
231
|
+
ctx = null;
|
|
232
|
+
}
|
|
233
|
+
let tick = 0;
|
|
234
|
+
let destroyed = false;
|
|
235
|
+
const drawGraph = () => {
|
|
236
|
+
if (!ctx || !canvas)
|
|
237
|
+
return;
|
|
238
|
+
const w = canvas.width;
|
|
239
|
+
const hgt = canvas.height;
|
|
240
|
+
ctx.clearRect(0, 0, w, hgt);
|
|
241
|
+
const n = monitor.filled;
|
|
242
|
+
const win = monitor.window;
|
|
243
|
+
if (n === 0)
|
|
244
|
+
return;
|
|
245
|
+
let max = GRAPH_MIN_MS;
|
|
246
|
+
const hist = monitor.history;
|
|
247
|
+
for (let i = 0; i < n; i++)
|
|
248
|
+
if (hist[i] > max)
|
|
249
|
+
max = hist[i];
|
|
250
|
+
// 16.7 ms guide.
|
|
251
|
+
const guide = hgt - (hgt * (1000 / 60)) / max;
|
|
252
|
+
ctx.fillStyle = 'rgba(255, 255, 255, 0.18)';
|
|
253
|
+
ctx.fillRect(0, Math.round(guide), w, 1);
|
|
254
|
+
// Oldest to newest, left to right; a full ring starts at `head`.
|
|
255
|
+
const start = n < win ? 0 : monitor.head;
|
|
256
|
+
const barW = w / win;
|
|
257
|
+
ctx.fillStyle = 'rgba(224, 180, 90, 0.85)';
|
|
258
|
+
for (let i = 0; i < n; i++) {
|
|
259
|
+
const v = hist[(start + i) % win];
|
|
260
|
+
const bh = Math.max(1, (hgt * v) / max);
|
|
261
|
+
ctx.fillRect(i * barW, hgt - bh, Math.max(1, barW - 0.5), bh);
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
const render = () => {
|
|
265
|
+
const f = monitor.frame;
|
|
266
|
+
head.textContent = `${fmt(f.avg)} ms ${f.fps.toFixed(0)} fps`;
|
|
267
|
+
let text = `cpu last ${fmt(f.last)} p95 ${fmt(f.p95)} max ${fmt(f.max)}`;
|
|
268
|
+
const g = monitor.gpu;
|
|
269
|
+
if (g) {
|
|
270
|
+
text += `\ngpu ${fmt(g.total)} ms`;
|
|
271
|
+
for (const k in g.passes) {
|
|
272
|
+
const v = g.passes[k];
|
|
273
|
+
if (v > 0.005)
|
|
274
|
+
text += `\n ${k.padEnd(10)} ${fmt(v)}`;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
text += '\ngpu –';
|
|
279
|
+
}
|
|
280
|
+
const c = monitor.counts;
|
|
281
|
+
const parts = [];
|
|
282
|
+
for (const k in c) {
|
|
283
|
+
if (k === 'draws' && 'drawCalls' in c)
|
|
284
|
+
continue;
|
|
285
|
+
parts.push(`${k} ${c[k]}`);
|
|
286
|
+
}
|
|
287
|
+
if (parts.length > 0)
|
|
288
|
+
text += `\n${parts.join(' ')}`;
|
|
289
|
+
text += `\nscale ${monitor.resolutionScale.toFixed(2)}`;
|
|
290
|
+
if (monitor.memory !== undefined)
|
|
291
|
+
text += ` heap ${fmtBytes(monitor.memory)}`;
|
|
292
|
+
body.textContent = text;
|
|
293
|
+
drawGraph();
|
|
294
|
+
};
|
|
295
|
+
render();
|
|
296
|
+
const show = () => {
|
|
297
|
+
el.hidden = false;
|
|
298
|
+
tick = updateEvery - 1;
|
|
299
|
+
};
|
|
300
|
+
const hide = () => {
|
|
301
|
+
el.hidden = true;
|
|
302
|
+
};
|
|
303
|
+
return {
|
|
304
|
+
el,
|
|
305
|
+
update() {
|
|
306
|
+
if (destroyed || el.hidden)
|
|
307
|
+
return;
|
|
308
|
+
if (++tick < updateEvery)
|
|
309
|
+
return;
|
|
310
|
+
tick = 0;
|
|
311
|
+
render();
|
|
312
|
+
},
|
|
313
|
+
show,
|
|
314
|
+
hide,
|
|
315
|
+
toggle() {
|
|
316
|
+
if (el.hidden)
|
|
317
|
+
show();
|
|
318
|
+
else
|
|
319
|
+
hide();
|
|
320
|
+
},
|
|
321
|
+
get visible() {
|
|
322
|
+
return !el.hidden && el.isConnected;
|
|
323
|
+
},
|
|
324
|
+
destroy() {
|
|
325
|
+
destroyed = true;
|
|
326
|
+
el.remove();
|
|
327
|
+
},
|
|
328
|
+
};
|
|
329
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The "turn it sideways" card for a landscape game on a phone held upright (interim API: Phase 2's
|
|
3
|
+
* portrait layout regions retire it, D-041).
|
|
4
|
+
*
|
|
5
|
+
* The card covers the whole viewport, outside the scaled overlay (upright, the overlay is a thin
|
|
6
|
+
* strip), and goes the moment the phone turns. Nothing is paused: the game underneath just waits
|
|
7
|
+
* for input. All words are the game's, passed as options.
|
|
8
|
+
*
|
|
9
|
+
* It shows when the viewport is portrait and the device is a phone in hand: a handheld by
|
|
10
|
+
* `deviceTraits()`, or a finger on a phone-scale screen by the device mode. A narrow desktop
|
|
11
|
+
* window never gets it.
|
|
12
|
+
*/
|
|
13
|
+
import type { DeviceMode } from '../platform/device-mode.js';
|
|
14
|
+
/** The card's look. Each is a CSS value; omitted ones use neutral engine defaults. */
|
|
15
|
+
export interface RotateCardColors {
|
|
16
|
+
/** Card background (any `background` value, gradients included). */
|
|
17
|
+
background?: string;
|
|
18
|
+
text?: string;
|
|
19
|
+
/** Heading, phone outline and the note's tag. */
|
|
20
|
+
accent?: string;
|
|
21
|
+
/** The phone outline's glow. */
|
|
22
|
+
glow?: string;
|
|
23
|
+
/** The note line. */
|
|
24
|
+
muted?: string;
|
|
25
|
+
font?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface RotateCardOptions {
|
|
28
|
+
/** Heading, e.g. "Turn your phone sideways". Also the dialog's accessible name by default. */
|
|
29
|
+
title: string;
|
|
30
|
+
/** Body paragraphs under the heading. */
|
|
31
|
+
lines?: readonly string[];
|
|
32
|
+
/** An optional last line in italics, with a short bold tag before it (a speaker, say). */
|
|
33
|
+
note?: {
|
|
34
|
+
tag?: string;
|
|
35
|
+
text: string;
|
|
36
|
+
};
|
|
37
|
+
/** Accessible name. Default: `title`. */
|
|
38
|
+
ariaLabel?: string;
|
|
39
|
+
/** The device mode; a finger on a phone-scale screen counts as a phone in hand. */
|
|
40
|
+
device?: Pick<DeviceMode, 'phone' | 'touch' | 'onChange'>;
|
|
41
|
+
colors?: RotateCardColors;
|
|
42
|
+
/** Document to mount into. Default: the global `document`. */
|
|
43
|
+
doc?: Document;
|
|
44
|
+
}
|
|
45
|
+
export interface RotateCard {
|
|
46
|
+
/** The card (`role=dialog`), appended to `<body>`. */
|
|
47
|
+
readonly element: HTMLElement;
|
|
48
|
+
/** True while the card shows. */
|
|
49
|
+
readonly visible: boolean;
|
|
50
|
+
/** Re-check orientation. It also runs on resize, orientation change and device-mode change. */
|
|
51
|
+
update(): void;
|
|
52
|
+
destroy(): void;
|
|
53
|
+
}
|
|
54
|
+
export declare function createRotateCard(opts: RotateCardOptions): RotateCard;
|