@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,828 @@
|
|
|
1
|
+
import { WGSL_MOTION } from '../render/scene-pipelines.js';
|
|
2
|
+
import { WGSL_GLOBALS, WGSL_LIGHTS } from '../render/wgsl.js';
|
|
3
|
+
const COMMON = /* wgsl */ `
|
|
4
|
+
struct Particle {
|
|
5
|
+
posVel: vec4f, // pos.xy, vel.xy (logical px, px/s)
|
|
6
|
+
c0: vec4f, // colour at birth (linear, premultiplied by nothing)
|
|
7
|
+
c1: vec4f, // colour at death
|
|
8
|
+
life: vec4f, // age, lifetime, size at birth, size at death
|
|
9
|
+
phys: vec4f, // gravity.xy, drag, emissive
|
|
10
|
+
};
|
|
11
|
+
struct Sim {
|
|
12
|
+
dt: f32,
|
|
13
|
+
floorY: f32,
|
|
14
|
+
bounce: f32,
|
|
15
|
+
time: f32,
|
|
16
|
+
capacity: f32,
|
|
17
|
+
stretch: f32,
|
|
18
|
+
spawnCount: f32,
|
|
19
|
+
_pad: f32,
|
|
20
|
+
};
|
|
21
|
+
`;
|
|
22
|
+
export const PARTICLE_COMPUTE_WGSL = /* wgsl */ `
|
|
23
|
+
${COMMON}
|
|
24
|
+
struct Spawn {
|
|
25
|
+
a: vec4f, // x, y, shapeA, shapeB
|
|
26
|
+
b: vec4f, // angle, spread, speedMin, speedMax
|
|
27
|
+
c: vec4f, // lifeMin, lifeMax, sizeStart, sizeEnd
|
|
28
|
+
c0: vec4f,
|
|
29
|
+
c1: vec4f,
|
|
30
|
+
d: vec4f, // gravX, gravY, drag, shapeKind (0 point, 1 circle, 2 rect, 3 line)
|
|
31
|
+
e: vec4f, // start, count, seed, emissive
|
|
32
|
+
};
|
|
33
|
+
@group(0) @binding(0) var<storage, read_write> particles: array<Particle>;
|
|
34
|
+
@group(0) @binding(1) var<storage, read> spawns: array<Spawn>;
|
|
35
|
+
@group(0) @binding(2) var<uniform> sim: Sim;
|
|
36
|
+
|
|
37
|
+
fn pcg(v: u32) -> u32 {
|
|
38
|
+
let s = v * 747796405u + 2891336453u;
|
|
39
|
+
let w = ((s >> ((s >> 28u) + 4u)) ^ s) * 277803737u;
|
|
40
|
+
return (w >> 22u) ^ w;
|
|
41
|
+
}
|
|
42
|
+
fn rnd(state: ptr<function, u32>) -> f32 {
|
|
43
|
+
*state = pcg(*state);
|
|
44
|
+
return f32(*state) / 4294967295.0;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@compute @workgroup_size(64) fn spawn(@builtin(global_invocation_id) gid: vec3u) {
|
|
48
|
+
if (f32(gid.y) >= sim.spawnCount) { return; }
|
|
49
|
+
let cmd = spawns[gid.y];
|
|
50
|
+
let i = gid.x;
|
|
51
|
+
if (f32(i) >= cmd.e.y) { return; }
|
|
52
|
+
let cap = u32(sim.capacity);
|
|
53
|
+
let idx = (u32(cmd.e.x) + i) % cap;
|
|
54
|
+
var st = pcg(i ^ pcg(u32(cmd.e.z)));
|
|
55
|
+
|
|
56
|
+
var pos = cmd.a.xy;
|
|
57
|
+
let kind = cmd.d.w;
|
|
58
|
+
if (kind > 0.5 && kind < 1.5) {
|
|
59
|
+
let r = sqrt(rnd(&st)) * cmd.a.z;
|
|
60
|
+
let th = rnd(&st) * 6.2831853;
|
|
61
|
+
pos += vec2f(cos(th), sin(th)) * r;
|
|
62
|
+
} else if (kind > 1.5 && kind < 2.5) {
|
|
63
|
+
pos += (vec2f(rnd(&st), rnd(&st)) - 0.5) * cmd.a.zw;
|
|
64
|
+
} else if (kind > 2.5) {
|
|
65
|
+
pos += cmd.a.zw * rnd(&st);
|
|
66
|
+
}
|
|
67
|
+
let ang = cmd.b.x + (rnd(&st) - 0.5) * cmd.b.y;
|
|
68
|
+
let speed = mix(cmd.b.z, cmd.b.w, rnd(&st));
|
|
69
|
+
let life = mix(cmd.c.x, cmd.c.y, rnd(&st));
|
|
70
|
+
let sj = 0.7 + 0.6 * rnd(&st);
|
|
71
|
+
|
|
72
|
+
var p: Particle;
|
|
73
|
+
p.posVel = vec4f(pos, vec2f(cos(ang), sin(ang)) * speed);
|
|
74
|
+
p.c0 = cmd.c0;
|
|
75
|
+
p.c1 = cmd.c1;
|
|
76
|
+
p.life = vec4f(0.0, max(life, 1e-3), cmd.c.z * sj, cmd.c.w * sj);
|
|
77
|
+
p.phys = vec4f(cmd.d.xyz, cmd.e.w);
|
|
78
|
+
particles[idx] = p;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@compute @workgroup_size(64) fn update(@builtin(global_invocation_id) gid: vec3u) {
|
|
82
|
+
let i = gid.x;
|
|
83
|
+
if (f32(i) >= sim.capacity) { return; }
|
|
84
|
+
var p = particles[i];
|
|
85
|
+
if (p.life.x >= p.life.y) { return; }
|
|
86
|
+
let dt = sim.dt;
|
|
87
|
+
p.life.x += dt;
|
|
88
|
+
var v = p.posVel.zw + p.phys.xy * dt;
|
|
89
|
+
v *= exp(-p.phys.z * dt);
|
|
90
|
+
var pos = p.posVel.xy + v * dt;
|
|
91
|
+
if (pos.y > sim.floorY && v.y > 0.0) {
|
|
92
|
+
pos.y = sim.floorY;
|
|
93
|
+
v = vec2f(v.x * 0.75, -v.y * sim.bounce);
|
|
94
|
+
}
|
|
95
|
+
p.posVel = vec4f(pos, v);
|
|
96
|
+
particles[i] = p;
|
|
97
|
+
}
|
|
98
|
+
`;
|
|
99
|
+
export const PARTICLE_RENDER_WGSL = /* wgsl */ `
|
|
100
|
+
${WGSL_GLOBALS}
|
|
101
|
+
${COMMON}
|
|
102
|
+
@group(1) @binding(0) var<storage, read> particles: array<Particle>;
|
|
103
|
+
@group(1) @binding(1) var<uniform> sim: Sim;
|
|
104
|
+
|
|
105
|
+
struct PVOut {
|
|
106
|
+
@builtin(position) clip: vec4f,
|
|
107
|
+
@location(0) q: vec2f,
|
|
108
|
+
@location(1) color: vec4f,
|
|
109
|
+
@location(2) emissive: f32,
|
|
110
|
+
};
|
|
111
|
+
struct FragOut {
|
|
112
|
+
@location(0) color: vec4f,
|
|
113
|
+
@location(1) emissive: vec4f,
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
@vertex fn vs(@builtin(vertex_index) vi: u32, @builtin(instance_index) ii: u32) -> PVOut {
|
|
117
|
+
var o: PVOut;
|
|
118
|
+
let p = particles[ii];
|
|
119
|
+
if (p.life.x >= p.life.y) {
|
|
120
|
+
o.clip = vec4f(0.0, 0.0, 2.0, 1.0);
|
|
121
|
+
return o;
|
|
122
|
+
}
|
|
123
|
+
var corners = array<vec2f, 6>(
|
|
124
|
+
vec2f(-1.0, -1.0), vec2f(1.0, -1.0), vec2f(1.0, 1.0),
|
|
125
|
+
vec2f(-1.0, -1.0), vec2f(1.0, 1.0), vec2f(-1.0, 1.0)
|
|
126
|
+
);
|
|
127
|
+
let q = corners[vi];
|
|
128
|
+
let t = p.life.x / p.life.y;
|
|
129
|
+
let size = max(mix(p.life.z, p.life.w, t), 0.0);
|
|
130
|
+
let vel = p.posVel.zw;
|
|
131
|
+
let speed = length(vel);
|
|
132
|
+
var dir = vec2f(1.0, 0.0);
|
|
133
|
+
if (speed > 1e-3) { dir = vel / speed; }
|
|
134
|
+
let len = size + speed * sim.stretch;
|
|
135
|
+
let perp = vec2f(-dir.y, dir.x);
|
|
136
|
+
let world = p.posVel.xy + dir * (q.x * len * 0.5) + perp * (q.y * size * 0.5);
|
|
137
|
+
o.clip = camera.view_proj * vec4f(world, 0.0, 1.0);
|
|
138
|
+
o.q = q;
|
|
139
|
+
o.color = mix(p.c0, p.c1, t);
|
|
140
|
+
o.emissive = p.phys.w;
|
|
141
|
+
return o;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
@fragment fn fs(in: PVOut) -> FragOut {
|
|
145
|
+
let d = length(in.q);
|
|
146
|
+
var a = clamp(1.0 - d, 0.0, 1.0);
|
|
147
|
+
a = a * a * in.color.a;
|
|
148
|
+
let c = in.color.rgb * a;
|
|
149
|
+
var o: FragOut;
|
|
150
|
+
o.color = vec4f(c, a);
|
|
151
|
+
o.emissive = vec4f(c * in.emissive, a);
|
|
152
|
+
return o;
|
|
153
|
+
}
|
|
154
|
+
`;
|
|
155
|
+
// ------------------------------------------------------------------ the extended kernel
|
|
156
|
+
// One kernel for world space and for the extended 2D system (ws/fx-3): 3D positions (z = 0 in
|
|
157
|
+
// 2D), spin, sprite-sheet tiles, multi-stop curves, forces, sub-emission and mesh emission. The
|
|
158
|
+
// plain 2D shaders above stay byte-for-byte what they were (E-008: 2D particles pixel-identical).
|
|
159
|
+
/** Particles per `Particle` struct below: 10 vec4 = 160 bytes. */
|
|
160
|
+
export const FX_PARTICLE_FLOATS = 40;
|
|
161
|
+
/** Floats per extended spawn record (`Spawn` below). */
|
|
162
|
+
export const FX_SPAWN_FLOATS = 52;
|
|
163
|
+
/** Forces the extended sim block carries. */
|
|
164
|
+
export const MAX_FORCES = 8;
|
|
165
|
+
/** Floats of the extended sim block: 20 scalars + 8 forces × 3 vec4. */
|
|
166
|
+
export const FX_SIM_FLOATS = 20 + MAX_FORCES * 12;
|
|
167
|
+
/** Floats per size-curve slot (count, 8 stops, 3 pad) and per colour-curve slot (count + pad, 8 RGBA). */
|
|
168
|
+
export const SIZE_CURVE_FLOATS = 12;
|
|
169
|
+
export const COLOR_CURVE_FLOATS = 36;
|
|
170
|
+
/** Floats per sub-emission request (`Req` below). */
|
|
171
|
+
export const FX_REQUEST_FLOATS = 8;
|
|
172
|
+
/** Floats per mesh-surface triangle (p0 + cdf, p1, p2, n0, n1, n2). */
|
|
173
|
+
export const SURFACE_TRI_FLOATS = 24;
|
|
174
|
+
function fxCommon(world) {
|
|
175
|
+
return /* wgsl */ `
|
|
176
|
+
const WORLD_SPACE: bool = ${world};
|
|
177
|
+
struct Particle {
|
|
178
|
+
pos: vec4f, // xyz, rotation (rad)
|
|
179
|
+
vel: vec4f, // xyz, spin (rad/s)
|
|
180
|
+
c0: vec4f,
|
|
181
|
+
c1: vec4f,
|
|
182
|
+
life: vec4f, // age, lifetime, size at birth (or the size-curve scale), size at death
|
|
183
|
+
phys: vec4f, // gravity xyz, drag
|
|
184
|
+
wind: vec4f, // wind xyz, emissive
|
|
185
|
+
tile: vec4f, // tile from, tile to, tile rate (fps, 0 = over life), flags (1 align to velocity)
|
|
186
|
+
curve: vec4f, // size slot, colour slot, death slot, life slot (-1 none)
|
|
187
|
+
sub: vec4f, // death count, life rate, life carry, unused
|
|
188
|
+
};
|
|
189
|
+
struct Force {
|
|
190
|
+
a: vec4f, // kind (0 attract, 1 vortex, 2 turbulence, 3 drag), x, y, z
|
|
191
|
+
b: vec4f, // strength, radius, frequency, seed
|
|
192
|
+
c: vec4f, // vortex axis xyz | attract falloff (0 constant, 1 linear, 2 inverse-square), dead zone; drag amount
|
|
193
|
+
};
|
|
194
|
+
struct Sim {
|
|
195
|
+
dt: f32,
|
|
196
|
+
floorY: f32,
|
|
197
|
+
bounce: f32,
|
|
198
|
+
time: f32,
|
|
199
|
+
capacity: f32,
|
|
200
|
+
stretch: f32,
|
|
201
|
+
spawnCount: f32,
|
|
202
|
+
spawnTotal: f32,
|
|
203
|
+
fadeNear: f32,
|
|
204
|
+
fadeFar: f32,
|
|
205
|
+
softness: f32,
|
|
206
|
+
softOn: f32,
|
|
207
|
+
lit: f32,
|
|
208
|
+
textured: f32,
|
|
209
|
+
columns: f32,
|
|
210
|
+
rows: f32,
|
|
211
|
+
forceCount: f32,
|
|
212
|
+
colorBase: f32,
|
|
213
|
+
maxRequests: f32,
|
|
214
|
+
_pad: f32,
|
|
215
|
+
forces: array<Force, ${MAX_FORCES}>,
|
|
216
|
+
};
|
|
217
|
+
`;
|
|
218
|
+
}
|
|
219
|
+
const FX_RANDOM = /* wgsl */ `
|
|
220
|
+
fn pcg(v: u32) -> u32 {
|
|
221
|
+
let s = v * 747796405u + 2891336453u;
|
|
222
|
+
let w = ((s >> ((s >> 28u) + 4u)) ^ s) * 277803737u;
|
|
223
|
+
return (w >> 22u) ^ w;
|
|
224
|
+
}
|
|
225
|
+
fn rnd(state: ptr<function, u32>) -> f32 {
|
|
226
|
+
*state = pcg(*state);
|
|
227
|
+
return f32(*state) / 4294967295.0;
|
|
228
|
+
}
|
|
229
|
+
`;
|
|
230
|
+
/**
|
|
231
|
+
* The extended compute kernel: \`spawn\` (CPU emits), \`advance\` (one thread: the ring head
|
|
232
|
+
* moves past this frame's emits and the sub-emission request count resets), \`update\`
|
|
233
|
+
* (integrate, forces, floor, spin, sub-emission requests) and \`subspawn\` (one thread per
|
|
234
|
+
* request: the children, one level deep).
|
|
235
|
+
*/
|
|
236
|
+
function fxComputeWgsl(world) {
|
|
237
|
+
return /* wgsl */ `
|
|
238
|
+
${fxCommon(world)}
|
|
239
|
+
struct Spawn {
|
|
240
|
+
a: vec4f, // x, y, z, shapeKind (0 point, 1 circle, 2 rect, 3 line, 4 box, 5 sphere, 6 mesh)
|
|
241
|
+
b: vec4f, // shapeA, shapeB, shapeC, spread
|
|
242
|
+
c: vec4f, // angle, vz, speedMin, speedMax
|
|
243
|
+
d: vec4f, // lifeMin, lifeMax, sizeStart, sizeEnd
|
|
244
|
+
c0: vec4f,
|
|
245
|
+
c1: vec4f,
|
|
246
|
+
e: vec4f, // gravity xyz, drag
|
|
247
|
+
f: vec4f, // wind xyz, emissive
|
|
248
|
+
g: vec4f, // start (relative to the frame's head), count, seed, inherit
|
|
249
|
+
h: vec4f, // tile from, tile to, tile rate, flags (1 align to velocity, 2 along normal)
|
|
250
|
+
i: vec4f, // rotation min, max, spin min, max
|
|
251
|
+
j: vec4f, // size slot, colour slot, death slot, life slot
|
|
252
|
+
k: vec4f, // death count, life rate, mesh offset (vec4s), mesh triangles
|
|
253
|
+
};
|
|
254
|
+
struct Req {
|
|
255
|
+
pos: vec4f, // xyz, slot
|
|
256
|
+
vel: vec4f, // xyz (already scaled by inherit), count
|
|
257
|
+
};
|
|
258
|
+
struct State {
|
|
259
|
+
head: atomic<u32>,
|
|
260
|
+
count: atomic<u32>,
|
|
261
|
+
_a: u32,
|
|
262
|
+
_b: u32,
|
|
263
|
+
reqs: array<Req>,
|
|
264
|
+
};
|
|
265
|
+
@group(0) @binding(0) var<storage, read_write> particles: array<Particle>;
|
|
266
|
+
@group(0) @binding(1) var<storage, read> spawns: array<Spawn>;
|
|
267
|
+
@group(0) @binding(2) var<uniform> sim: Sim;
|
|
268
|
+
@group(0) @binding(3) var<storage, read_write> state: State;
|
|
269
|
+
@group(0) @binding(4) var<storage, read> subs: array<Spawn>;
|
|
270
|
+
@group(0) @binding(5) var<storage, read> surface: array<vec4f>;
|
|
271
|
+
${FX_RANDOM}
|
|
272
|
+
// A uniform direction in the cone of half-angle \`half\` around the unit vector \`axis\`.
|
|
273
|
+
fn cone(axis: vec3f, half: f32, st: ptr<function, u32>) -> vec3f {
|
|
274
|
+
let cz = mix(cos(half), 1.0, rnd(st));
|
|
275
|
+
let sz = sqrt(max(1.0 - cz * cz, 0.0));
|
|
276
|
+
let ph = rnd(st) * 6.2831853;
|
|
277
|
+
var t1 = cross(axis, vec3f(0.0, 0.0, 1.0));
|
|
278
|
+
if (dot(t1, t1) < 1e-6) { t1 = vec3f(1.0, 0.0, 0.0); }
|
|
279
|
+
t1 = normalize(t1);
|
|
280
|
+
let t2 = cross(axis, t1);
|
|
281
|
+
return axis * cz + (t1 * cos(ph) + t2 * sin(ph)) * sz;
|
|
282
|
+
}
|
|
283
|
+
// Launch direction: a 2D fan of \`spread\` around \`axis\` in the XY plane, or a 3D cone.
|
|
284
|
+
fn launch(axis: vec3f, spread: f32, st: ptr<function, u32>) -> vec3f {
|
|
285
|
+
if (WORLD_SPACE) {
|
|
286
|
+
return cone(axis, clamp(spread * 0.5, 0.0, 3.14159265), st);
|
|
287
|
+
}
|
|
288
|
+
let base = atan2(axis.y, axis.x);
|
|
289
|
+
let ang = base + (rnd(st) - 0.5) * spread;
|
|
290
|
+
return vec3f(cos(ang), sin(ang), 0.0);
|
|
291
|
+
}
|
|
292
|
+
// Value noise on the integer lattice, seeded; and the curl of three such fields.
|
|
293
|
+
fn lattice(p: vec3i, seed: u32) -> f32 {
|
|
294
|
+
let h = pcg((u32(p.x) * 73856093u) ^ (u32(p.y) * 19349663u) ^ (u32(p.z) * 83492791u) ^ seed);
|
|
295
|
+
return f32(h) / 4294967295.0;
|
|
296
|
+
}
|
|
297
|
+
fn vnoise(p: vec3f, seed: u32) -> f32 {
|
|
298
|
+
let i = vec3i(floor(p));
|
|
299
|
+
let f = fract(p);
|
|
300
|
+
let u = f * f * (3.0 - 2.0 * f);
|
|
301
|
+
let c000 = lattice(i, seed);
|
|
302
|
+
let c100 = lattice(i + vec3i(1, 0, 0), seed);
|
|
303
|
+
let c010 = lattice(i + vec3i(0, 1, 0), seed);
|
|
304
|
+
let c110 = lattice(i + vec3i(1, 1, 0), seed);
|
|
305
|
+
let c001 = lattice(i + vec3i(0, 0, 1), seed);
|
|
306
|
+
let c101 = lattice(i + vec3i(1, 0, 1), seed);
|
|
307
|
+
let c011 = lattice(i + vec3i(0, 1, 1), seed);
|
|
308
|
+
let c111 = lattice(i + vec3i(1, 1, 1), seed);
|
|
309
|
+
let x00 = mix(c000, c100, u.x);
|
|
310
|
+
let x10 = mix(c010, c110, u.x);
|
|
311
|
+
let x01 = mix(c001, c101, u.x);
|
|
312
|
+
let x11 = mix(c011, c111, u.x);
|
|
313
|
+
return mix(mix(x00, x10, u.y), mix(x01, x11, u.y), u.z);
|
|
314
|
+
}
|
|
315
|
+
fn curl(p: vec3f, seed: u32) -> vec3f {
|
|
316
|
+
let e = 0.05;
|
|
317
|
+
let dx = vec3f(e, 0.0, 0.0);
|
|
318
|
+
let dy = vec3f(0.0, e, 0.0);
|
|
319
|
+
let dz = vec3f(0.0, 0.0, e);
|
|
320
|
+
let sx = seed;
|
|
321
|
+
let sy = seed + 1u;
|
|
322
|
+
let sz = seed + 2u;
|
|
323
|
+
let dzdy = vnoise(p + dy, sz) - vnoise(p - dy, sz);
|
|
324
|
+
let dydz = vnoise(p + dz, sy) - vnoise(p - dz, sy);
|
|
325
|
+
let dxdz = vnoise(p + dz, sx) - vnoise(p - dz, sx);
|
|
326
|
+
let dzdx = vnoise(p + dx, sz) - vnoise(p - dx, sz);
|
|
327
|
+
let dydx = vnoise(p + dx, sy) - vnoise(p - dx, sy);
|
|
328
|
+
let dxdy = vnoise(p + dy, sx) - vnoise(p - dy, sx);
|
|
329
|
+
return vec3f(dzdy - dydz, dxdz - dzdx, dydx - dxdy) / (2.0 * e);
|
|
330
|
+
}
|
|
331
|
+
// Binary search of the surface's cumulative-area table: the triangle at \`u\`.
|
|
332
|
+
fn surface_tri(offset: u32, count: u32, u: f32) -> u32 {
|
|
333
|
+
var lo = 0u;
|
|
334
|
+
var hi = count - 1u;
|
|
335
|
+
for (var k = 0u; k < 32u; k++) {
|
|
336
|
+
if (lo >= hi) { break; }
|
|
337
|
+
let mid = (lo + hi) / 2u;
|
|
338
|
+
if (surface[offset + mid * 6u].w < u) { lo = mid + 1u; } else { hi = mid; }
|
|
339
|
+
}
|
|
340
|
+
return lo;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// A particle from a spawn record: at \`origin\` + the shape, with \`inherit\` added to its launch.
|
|
344
|
+
fn make(cmd: Spawn, origin: vec3f, inherit: vec3f, idx: u32, st: ptr<function, u32>, child: bool) {
|
|
345
|
+
var pos = origin + cmd.a.xyz;
|
|
346
|
+
let kind = cmd.a.w;
|
|
347
|
+
let s = cmd.b.xyz;
|
|
348
|
+
var axis = vec3f(cos(cmd.c.x), sin(cmd.c.x), 0.0);
|
|
349
|
+
if (kind > 0.5 && kind < 1.5) {
|
|
350
|
+
let r = sqrt(rnd(st)) * s.x;
|
|
351
|
+
let th = rnd(st) * 6.2831853;
|
|
352
|
+
pos += vec3f(cos(th), sin(th), 0.0) * r;
|
|
353
|
+
} else if (kind > 1.5 && kind < 2.5) {
|
|
354
|
+
pos += vec3f((vec2f(rnd(st), rnd(st)) - 0.5) * s.xy, 0.0);
|
|
355
|
+
} else if (kind > 2.5 && kind < 3.5) {
|
|
356
|
+
pos += s * rnd(st);
|
|
357
|
+
} else if (kind > 3.5 && kind < 4.5) {
|
|
358
|
+
pos += (vec3f(rnd(st), rnd(st), rnd(st)) - 0.5) * s;
|
|
359
|
+
} else if (kind > 4.5 && kind < 5.5) {
|
|
360
|
+
let r = pow(rnd(st), 1.0 / 3.0) * s.x;
|
|
361
|
+
pos += cone(vec3f(0.0, 1.0, 0.0), 3.14159265, st) * r;
|
|
362
|
+
} else if (kind > 5.5) {
|
|
363
|
+
let count = u32(cmd.k.w);
|
|
364
|
+
if (count > 0u) {
|
|
365
|
+
let base = u32(cmd.k.z);
|
|
366
|
+
let tri = base + surface_tri(base, count, rnd(st)) * 6u;
|
|
367
|
+
let r1 = sqrt(rnd(st));
|
|
368
|
+
let r2 = rnd(st);
|
|
369
|
+
let w = vec3f(1.0 - r1, r1 * (1.0 - r2), r1 * r2);
|
|
370
|
+
pos += surface[tri].xyz * w.x + surface[tri + 1u].xyz * w.y + surface[tri + 2u].xyz * w.z;
|
|
371
|
+
if ((u32(cmd.h.w) & 2u) != 0u) {
|
|
372
|
+
let n = surface[tri + 3u].xyz * w.x + surface[tri + 4u].xyz * w.y + surface[tri + 5u].xyz * w.z;
|
|
373
|
+
if (dot(n, n) > 1e-8) { axis = normalize(n); }
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
let dir = launch(axis, cmd.b.w, st);
|
|
378
|
+
let speed = mix(cmd.c.z, cmd.c.w, rnd(st));
|
|
379
|
+
let life = mix(cmd.d.x, cmd.d.y, rnd(st));
|
|
380
|
+
let sj = 0.7 + 0.6 * rnd(st);
|
|
381
|
+
let rot = mix(cmd.i.x, cmd.i.y, rnd(st));
|
|
382
|
+
let spin = mix(cmd.i.z, cmd.i.w, rnd(st));
|
|
383
|
+
|
|
384
|
+
var p: Particle;
|
|
385
|
+
p.pos = vec4f(pos, rot);
|
|
386
|
+
p.vel = vec4f(dir * speed + vec3f(0.0, 0.0, cmd.c.y) + inherit, spin);
|
|
387
|
+
p.c0 = cmd.c0;
|
|
388
|
+
p.c1 = cmd.c1;
|
|
389
|
+
if (cmd.j.x >= 0.0) {
|
|
390
|
+
p.life = vec4f(0.0, max(life, 1e-3), sj, sj);
|
|
391
|
+
} else {
|
|
392
|
+
p.life = vec4f(0.0, max(life, 1e-3), cmd.d.z * sj, cmd.d.w * sj);
|
|
393
|
+
}
|
|
394
|
+
p.phys = cmd.e;
|
|
395
|
+
p.wind = cmd.f;
|
|
396
|
+
p.tile = cmd.h;
|
|
397
|
+
if (child) {
|
|
398
|
+
p.curve = vec4f(cmd.j.xy, -1.0, -1.0);
|
|
399
|
+
p.sub = vec4f(0.0);
|
|
400
|
+
} else {
|
|
401
|
+
p.curve = cmd.j;
|
|
402
|
+
p.sub = vec4f(cmd.k.x, cmd.k.y, 0.0, 0.0);
|
|
403
|
+
}
|
|
404
|
+
particles[idx] = p;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
@compute @workgroup_size(64) fn spawn(@builtin(global_invocation_id) gid: vec3u) {
|
|
408
|
+
if (f32(gid.y) >= sim.spawnCount) { return; }
|
|
409
|
+
let cmd = spawns[gid.y];
|
|
410
|
+
let i = gid.x;
|
|
411
|
+
if (f32(i) >= cmd.g.y) { return; }
|
|
412
|
+
let cap = u32(sim.capacity);
|
|
413
|
+
let idx = (atomicLoad(&state.head) + u32(cmd.g.x) + i) % cap;
|
|
414
|
+
var st = pcg(i ^ pcg(u32(cmd.g.z)));
|
|
415
|
+
make(cmd, vec3f(0.0), vec3f(0.0), idx, &st, false);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
@compute @workgroup_size(1) fn advance() {
|
|
419
|
+
let cap = u32(sim.capacity);
|
|
420
|
+
let h = atomicLoad(&state.head);
|
|
421
|
+
atomicStore(&state.head, (h + u32(sim.spawnTotal)) % cap);
|
|
422
|
+
atomicStore(&state.count, 0u);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
fn request(pos: vec3f, vel: vec3f, slot: f32, count: f32) {
|
|
426
|
+
let r = atomicAdd(&state.count, 1u);
|
|
427
|
+
if (f32(r) >= sim.maxRequests) { return; }
|
|
428
|
+
let cmd = subs[u32(slot)];
|
|
429
|
+
var q: Req;
|
|
430
|
+
q.pos = vec4f(pos, slot);
|
|
431
|
+
q.vel = vec4f(vel * cmd.g.w, count);
|
|
432
|
+
state.reqs[r] = q;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
@compute @workgroup_size(64) fn update(@builtin(global_invocation_id) gid: vec3u) {
|
|
436
|
+
let i = gid.x;
|
|
437
|
+
if (f32(i) >= sim.capacity) { return; }
|
|
438
|
+
var p = particles[i];
|
|
439
|
+
if (p.life.x >= p.life.y) { return; }
|
|
440
|
+
let dt = sim.dt;
|
|
441
|
+
p.life.x += dt;
|
|
442
|
+
var pos = p.pos.xyz;
|
|
443
|
+
var v = p.vel.xyz;
|
|
444
|
+
var acc = p.phys.xyz;
|
|
445
|
+
var drag = p.phys.w;
|
|
446
|
+
for (var k = 0u; k < u32(sim.forceCount); k++) {
|
|
447
|
+
let f = sim.forces[k];
|
|
448
|
+
let kind = f.a.x;
|
|
449
|
+
if (kind < 0.5) {
|
|
450
|
+
let d = f.a.yzw - pos;
|
|
451
|
+
let r = length(d);
|
|
452
|
+
var w = select(1.0, max(1.0 - r / f.b.y, 0.0), f.b.y > 0.0);
|
|
453
|
+
if (f.c.x > 1.5) { w /= max(r * r, 1e-4); } else if (f.c.x > 0.5) { w *= r; }
|
|
454
|
+
if (r > 1e-4 && r >= f.c.y) { acc += d / r * f.b.x * w; }
|
|
455
|
+
} else if (kind < 1.5) {
|
|
456
|
+
let rel = pos - f.a.yzw;
|
|
457
|
+
let ax = normalize(f.c.xyz);
|
|
458
|
+
let radial = rel - ax * dot(rel, ax);
|
|
459
|
+
let r = length(radial);
|
|
460
|
+
let w = select(1.0, max(1.0 - r / f.b.y, 0.0), f.b.y > 0.0);
|
|
461
|
+
if (r > 1e-4) { acc += cross(ax, radial / r) * f.b.x * w; }
|
|
462
|
+
} else if (kind < 2.5) {
|
|
463
|
+
acc += curl(pos * f.b.z, u32(f.b.w)) * f.b.x;
|
|
464
|
+
} else {
|
|
465
|
+
drag += f.c.w;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
let wind = p.wind.xyz;
|
|
469
|
+
v += acc * dt;
|
|
470
|
+
v = wind + (v - wind) * exp(-drag * dt);
|
|
471
|
+
pos += v * dt;
|
|
472
|
+
if (WORLD_SPACE) {
|
|
473
|
+
if (pos.y < sim.floorY && v.y < 0.0) {
|
|
474
|
+
pos.y = sim.floorY;
|
|
475
|
+
v = vec3f(v.x * 0.75, -v.y * sim.bounce, v.z * 0.75);
|
|
476
|
+
}
|
|
477
|
+
} else {
|
|
478
|
+
if (pos.y > sim.floorY && v.y > 0.0) {
|
|
479
|
+
pos.y = sim.floorY;
|
|
480
|
+
v = vec3f(v.x * 0.75, -v.y * sim.bounce, 0.0);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
p.pos = vec4f(pos, p.pos.w + p.vel.w * dt);
|
|
484
|
+
p.vel = vec4f(v, p.vel.w);
|
|
485
|
+
// Sub-emission: a trail while alive, a burst on the frame the particle dies.
|
|
486
|
+
if (p.curve.w >= 0.0 && p.sub.y > 0.0) {
|
|
487
|
+
p.sub.z += p.sub.y * dt;
|
|
488
|
+
let n = floor(p.sub.z);
|
|
489
|
+
if (n >= 1.0) {
|
|
490
|
+
p.sub.z -= n;
|
|
491
|
+
request(pos, v, p.curve.w, n);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
if (p.life.x >= p.life.y && p.curve.z >= 0.0 && p.sub.x >= 1.0) {
|
|
495
|
+
request(pos, v, p.curve.z, p.sub.x);
|
|
496
|
+
}
|
|
497
|
+
particles[i] = p;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
@compute @workgroup_size(64) fn subspawn(@builtin(global_invocation_id) gid: vec3u) {
|
|
501
|
+
let r = gid.x;
|
|
502
|
+
let pending = min(atomicLoad(&state.count), u32(sim.maxRequests));
|
|
503
|
+
if (r >= pending) { return; }
|
|
504
|
+
let q = state.reqs[r];
|
|
505
|
+
let cmd = subs[u32(q.pos.w)];
|
|
506
|
+
let n = u32(q.vel.w);
|
|
507
|
+
let cap = u32(sim.capacity);
|
|
508
|
+
let base = atomicAdd(&state.head, n);
|
|
509
|
+
var st = pcg((r * 7919u) ^ pcg(u32(sim.time * 1000.0)) ^ pcg(base));
|
|
510
|
+
for (var i = 0u; i < n; i++) {
|
|
511
|
+
make(cmd, q.pos.xyz, q.vel.xyz, (base + i) % cap, &st, true);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
`;
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* The extended render kernel: the quad is built from the view rows (world) or the XY axes (2D),
|
|
518
|
+
* aligned to the velocity (streaks) when the particle asks, rotated by its spin, sized and
|
|
519
|
+
* coloured by the curve table or the two-stop sugar, textured from the sprite sheet tile when
|
|
520
|
+
* the system has one (both blend modes; premultiplied on output), with the world-space fades
|
|
521
|
+
* and lighting of before.
|
|
522
|
+
*/
|
|
523
|
+
/**
|
|
524
|
+
* The extended render shader; with `velocity` (the velocity attachment's fragment location, 2
|
|
525
|
+
* or 3; world space only) the build that writes its own motion (ws/render-11): each corner's
|
|
526
|
+
* previous position is `world - vel * sim.dt` (this frame's step back), projected through
|
|
527
|
+
* `camera.prev_view_proj`, and the fragment writes `vec4f(engine_motion(..), 0, alpha)`.
|
|
528
|
+
* `velocity` 0 is the exported strings exactly.
|
|
529
|
+
*/
|
|
530
|
+
export function fxRenderWgsl(world, velocity = 0) {
|
|
531
|
+
const v = world && velocity > 0;
|
|
532
|
+
return /* wgsl */ `
|
|
533
|
+
${WGSL_GLOBALS}${v ? WGSL_MOTION : ''}
|
|
534
|
+
${world ? WGSL_LIGHTS : ''}
|
|
535
|
+
${fxCommon(world)}
|
|
536
|
+
@group(1) @binding(0) var<storage, read> particles: array<Particle>;
|
|
537
|
+
@group(1) @binding(1) var<uniform> sim: Sim;
|
|
538
|
+
${world ? '@group(1) @binding(2) var scene_depth: texture_depth_2d;' : ''}
|
|
539
|
+
@group(1) @binding(3) var<storage, read> curves: array<f32>;
|
|
540
|
+
@group(1) @binding(4) var sheet: texture_2d<f32>;
|
|
541
|
+
@group(1) @binding(5) var sheet_samp: sampler;
|
|
542
|
+
|
|
543
|
+
struct PVOut {
|
|
544
|
+
@builtin(position) clip: vec4f,
|
|
545
|
+
@location(0) q: vec2f,
|
|
546
|
+
@location(1) color: vec4f,
|
|
547
|
+
@location(2) emissive: f32,
|
|
548
|
+
@location(3) world: vec3f,
|
|
549
|
+
@location(4) view_z: f32,
|
|
550
|
+
@location(5) side: vec3f,
|
|
551
|
+
@location(6) uv: vec2f,${v ? '\n @location(7) prev_clip: vec4f,' : ''}
|
|
552
|
+
};
|
|
553
|
+
struct FragOut {
|
|
554
|
+
@location(0) color: vec4f,
|
|
555
|
+
@location(1) emissive: vec4f,${v ? `\n @location(${velocity}) velocity: vec4f,` : ''}
|
|
556
|
+
};
|
|
557
|
+
|
|
558
|
+
fn view_row(r: u32) -> vec3f {
|
|
559
|
+
return vec3f(camera.view[0][r], camera.view[1][r], camera.view[2][r]);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
// Piecewise-linear over n stops spread evenly on 0..1.
|
|
563
|
+
fn size_at(slot: f32, t: f32, s0: f32, s1: f32) -> f32 {
|
|
564
|
+
if (slot < 0.0) { return mix(s0, s1, t); }
|
|
565
|
+
let base = u32(slot) * ${SIZE_CURVE_FLOATS}u;
|
|
566
|
+
let n = u32(curves[base]);
|
|
567
|
+
let x = clamp(t, 0.0, 1.0) * f32(n - 1u);
|
|
568
|
+
let i = min(u32(floor(x)), n - 2u);
|
|
569
|
+
return mix(curves[base + 1u + i], curves[base + 2u + i], x - f32(i)) * s0;
|
|
570
|
+
}
|
|
571
|
+
fn color_at(slot: f32, t: f32, c0: vec4f, c1: vec4f) -> vec4f {
|
|
572
|
+
if (slot < 0.0) { return mix(c0, c1, t); }
|
|
573
|
+
let base = u32(sim.colorBase) + u32(slot) * ${COLOR_CURVE_FLOATS}u;
|
|
574
|
+
let n = u32(curves[base]);
|
|
575
|
+
let x = clamp(t, 0.0, 1.0) * f32(n - 1u);
|
|
576
|
+
let i = min(u32(floor(x)), n - 2u);
|
|
577
|
+
let a = base + 4u + i * 4u;
|
|
578
|
+
let ca = vec4f(curves[a], curves[a + 1u], curves[a + 2u], curves[a + 3u]);
|
|
579
|
+
let cb = vec4f(curves[a + 4u], curves[a + 5u], curves[a + 6u], curves[a + 7u]);
|
|
580
|
+
return mix(ca, cb, x - f32(i));
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
@vertex fn vs(@builtin(vertex_index) vi: u32, @builtin(instance_index) ii: u32) -> PVOut {
|
|
584
|
+
var o: PVOut;
|
|
585
|
+
let p = particles[ii];
|
|
586
|
+
if (p.life.x >= p.life.y) {
|
|
587
|
+
o.clip = vec4f(0.0, 0.0, 2.0, 1.0);
|
|
588
|
+
return o;
|
|
589
|
+
}
|
|
590
|
+
var corners = array<vec2f, 6>(
|
|
591
|
+
vec2f(-1.0, -1.0), vec2f(1.0, -1.0), vec2f(1.0, 1.0),
|
|
592
|
+
vec2f(-1.0, -1.0), vec2f(1.0, 1.0), vec2f(-1.0, 1.0)
|
|
593
|
+
);
|
|
594
|
+
let q = corners[vi];
|
|
595
|
+
let t = p.life.x / p.life.y;
|
|
596
|
+
let size = max(size_at(p.curve.x, t, p.life.z, p.life.w), 0.0);
|
|
597
|
+
var right = vec3f(1.0, 0.0, 0.0);
|
|
598
|
+
var up = vec3f(0.0, 1.0, 0.0);
|
|
599
|
+
if (WORLD_SPACE) {
|
|
600
|
+
right = view_row(0u);
|
|
601
|
+
up = view_row(1u);
|
|
602
|
+
}
|
|
603
|
+
let vel = p.vel.xyz;
|
|
604
|
+
let v2 = vec2f(dot(vel, right), dot(vel, up));
|
|
605
|
+
let speed = length(v2);
|
|
606
|
+
var dir = vec2f(1.0, 0.0);
|
|
607
|
+
var len = size;
|
|
608
|
+
if ((u32(p.tile.w) & 1u) != 0u) {
|
|
609
|
+
if (speed > 1e-4) { dir = v2 / speed; }
|
|
610
|
+
len = size + speed * sim.stretch;
|
|
611
|
+
}
|
|
612
|
+
let cr = cos(p.pos.w);
|
|
613
|
+
let sr = sin(p.pos.w);
|
|
614
|
+
dir = vec2f(dir.x * cr - dir.y * sr, dir.x * sr + dir.y * cr);
|
|
615
|
+
let axis = right * dir.x + up * dir.y;
|
|
616
|
+
let perp = up * dir.x - right * dir.y;
|
|
617
|
+
let world = p.pos.xyz + axis * (q.x * len * 0.5) + perp * (q.y * size * 0.5);
|
|
618
|
+
o.clip = camera.view_proj * vec4f(world, 1.0);${v ? '\n o.prev_clip = camera.prev_view_proj * vec4f(world - vel * sim.dt, 1.0);' : ''}
|
|
619
|
+
o.q = q;
|
|
620
|
+
var color = color_at(p.curve.y, t, p.c0, p.c1);
|
|
621
|
+
if (WORLD_SPACE && sim.fadeFar > sim.fadeNear) {
|
|
622
|
+
let d = length(world - camera.camera_pos.xyz);
|
|
623
|
+
color.a *= 1.0 - smoothstep(sim.fadeNear, sim.fadeFar, d);
|
|
624
|
+
}
|
|
625
|
+
o.color = color;
|
|
626
|
+
o.emissive = p.wind.w;
|
|
627
|
+
o.world = world;
|
|
628
|
+
o.view_z = (camera.view * vec4f(world, 1.0)).z;
|
|
629
|
+
o.side = axis * q.x + perp * q.y;
|
|
630
|
+
// Sprite sheet tile: a fixed tile, a flipbook over life, or at a fixed rate looping.
|
|
631
|
+
let frames = max(p.tile.y - p.tile.x + 1.0, 1.0);
|
|
632
|
+
var tile = p.tile.x;
|
|
633
|
+
if (p.tile.z > 0.0) {
|
|
634
|
+
tile += floor(p.life.x * p.tile.z) % frames;
|
|
635
|
+
} else {
|
|
636
|
+
tile += min(floor(t * frames), frames - 1.0);
|
|
637
|
+
}
|
|
638
|
+
let col = tile % sim.columns;
|
|
639
|
+
let row = floor(tile / sim.columns);
|
|
640
|
+
let qv = select(0.5 - q.y * 0.5, 0.5 + q.y * 0.5, !WORLD_SPACE);
|
|
641
|
+
o.uv = (vec2f(col, row) + vec2f(q.x * 0.5 + 0.5, qv)) / vec2f(sim.columns, sim.rows);
|
|
642
|
+
return o;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
// View-space z of a depth-buffer value under camera.proj (any perspective or ortho matrix).
|
|
646
|
+
fn view_z_of(d: f32) -> f32 {
|
|
647
|
+
let p = camera.proj;
|
|
648
|
+
let den = d * p[2][3] - p[2][2];
|
|
649
|
+
return (p[3][2] - d * p[3][3]) / select(den, 1e-6, abs(den) < 1e-6);
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
@fragment fn fs(in: PVOut) -> FragOut {
|
|
653
|
+
let tex = textureSample(sheet, sheet_samp, in.uv);
|
|
654
|
+
var a: f32;
|
|
655
|
+
var rgb: vec3f;
|
|
656
|
+
if (sim.textured > 0.5) {
|
|
657
|
+
a = in.color.a * tex.a;
|
|
658
|
+
rgb = in.color.rgb * tex.rgb;
|
|
659
|
+
} else {
|
|
660
|
+
let r = length(in.q);
|
|
661
|
+
a = clamp(1.0 - r, 0.0, 1.0);
|
|
662
|
+
a = a * a * in.color.a;
|
|
663
|
+
rgb = in.color.rgb;
|
|
664
|
+
}
|
|
665
|
+
${world
|
|
666
|
+
? /* wgsl */ `
|
|
667
|
+
if (sim.softOn > 0.5) {
|
|
668
|
+
let dims = vec2i(textureDimensions(scene_depth));
|
|
669
|
+
let px = clamp(vec2i(in.clip.xy), vec2i(0), dims - 1);
|
|
670
|
+
let scene_z = view_z_of(textureLoad(scene_depth, px, 0));
|
|
671
|
+
a *= saturate(abs(scene_z - in.view_z) / max(sim.softness, 1e-4));
|
|
672
|
+
}
|
|
673
|
+
if (sim.lit > 0.5) {
|
|
674
|
+
let back = view_row(2u);
|
|
675
|
+
let n = normalize(in.side + back * sqrt(max(1.0 - dot(in.side, in.side), 0.0)));
|
|
676
|
+
rgb *= engine_lambert(n, in.world);
|
|
677
|
+
}`
|
|
678
|
+
: ''}
|
|
679
|
+
let c = rgb * a;
|
|
680
|
+
var o: FragOut;
|
|
681
|
+
o.color = vec4f(c, a);
|
|
682
|
+
o.emissive = vec4f(c * in.emissive, a);${v ? '\n o.velocity = vec4f(engine_motion(in.clip, in.prev_clip), 0.0, a);' : ''}
|
|
683
|
+
return o;
|
|
684
|
+
}
|
|
685
|
+
`;
|
|
686
|
+
}
|
|
687
|
+
/** The extended 2D kernel (`extended: true`, or implied by `texture`, `forces`, `collide`). */
|
|
688
|
+
export const PARTICLE_EXT_COMPUTE_WGSL = fxComputeWgsl(false);
|
|
689
|
+
export const PARTICLE_EXT_RENDER_WGSL = fxRenderWgsl(false);
|
|
690
|
+
/** World space: the same extended kernel with a 3D cone launch, y-up floor and the view-row quad. */
|
|
691
|
+
export const PARTICLE_WORLD_COMPUTE_WGSL = fxComputeWgsl(true);
|
|
692
|
+
export const PARTICLE_WORLD_RENDER_WGSL = fxRenderWgsl(true);
|
|
693
|
+
/**
|
|
694
|
+
* Depth collision (world space, \`readsDepth\` + \`collide\`): runs in the depth hook over this
|
|
695
|
+
* frame's opaque depth. A live particle whose projected depth lies behind the scene by less
|
|
696
|
+
* than \`thickness\` (view-ray units) is pushed out along the surface normal (from the depth's
|
|
697
|
+
* neighbours) onto the surface, so a resting particle does not creep along the view ray, and its
|
|
698
|
+
* velocity is reflected about that normal, scaled by
|
|
699
|
+
* \`bounce\` normally and by \`1 - friction\` tangentially. A particle deeper than that is
|
|
700
|
+
* swept: if its position one step back (\`pos - vel * dt\`) was in front of the scene it
|
|
701
|
+
* crossed the surface this frame and is put back there, so a slow frame does not tunnel.
|
|
702
|
+
*/
|
|
703
|
+
export const PARTICLE_COLLIDE_WGSL = /* wgsl */ `
|
|
704
|
+
${fxCommon(true)}
|
|
705
|
+
struct Collide {
|
|
706
|
+
view_proj: mat4x4f,
|
|
707
|
+
inv_view_proj: mat4x4f,
|
|
708
|
+
viewport: vec4f, // x, y, width, height (target px)
|
|
709
|
+
params: vec4f, // bounce, friction, thickness, last step dt
|
|
710
|
+
};
|
|
711
|
+
@group(0) @binding(0) var<storage, read_write> particles: array<Particle>;
|
|
712
|
+
@group(0) @binding(1) var<uniform> col: Collide;
|
|
713
|
+
@group(0) @binding(2) var scene_depth: texture_depth_2d;
|
|
714
|
+
|
|
715
|
+
fn unproject(ndc: vec2f, d: f32) -> vec3f {
|
|
716
|
+
let w = col.inv_view_proj * vec4f(ndc, d, 1.0);
|
|
717
|
+
return w.xyz / w.w;
|
|
718
|
+
}
|
|
719
|
+
fn scene_at(px: vec2i, dims: vec2i) -> vec3f {
|
|
720
|
+
let c = clamp(px, vec2i(0), dims - 1);
|
|
721
|
+
let d = textureLoad(scene_depth, c, 0);
|
|
722
|
+
let ndc = (vec2f(c) + 0.5 - col.viewport.xy) / col.viewport.zw * vec2f(2.0, -2.0) + vec2f(-1.0, 1.0);
|
|
723
|
+
return unproject(ndc, d);
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
@compute @workgroup_size(64) fn collide(@builtin(global_invocation_id) gid: vec3u) {
|
|
727
|
+
let i = gid.x;
|
|
728
|
+
if (i >= arrayLength(&particles)) { return; }
|
|
729
|
+
var p = particles[i];
|
|
730
|
+
if (p.life.x >= p.life.y) { return; }
|
|
731
|
+
let clip = col.view_proj * vec4f(p.pos.xyz, 1.0);
|
|
732
|
+
if (clip.w <= 1e-5) { return; }
|
|
733
|
+
let ndc = clip.xyz / clip.w;
|
|
734
|
+
if (any(abs(ndc.xy) > vec2f(1.0)) || ndc.z < 0.0 || ndc.z > 1.0) { return; }
|
|
735
|
+
let dims = vec2i(textureDimensions(scene_depth));
|
|
736
|
+
let px = vec2i(col.viewport.xy + (ndc.xy * vec2f(0.5, -0.5) + 0.5) * col.viewport.zw);
|
|
737
|
+
let d = textureLoad(scene_depth, clamp(px, vec2i(0), dims - 1), 0);
|
|
738
|
+
if (d >= 1.0 || ndc.z <= d) { return; }
|
|
739
|
+
let near = unproject(ndc.xy, 0.0);
|
|
740
|
+
let hit = unproject(ndc.xy, d);
|
|
741
|
+
let ray = normalize(hit - near);
|
|
742
|
+
// The normal from pixel centres only: \`hit\` sits at the particle's sub-pixel position with the
|
|
743
|
+
// centre's depth, off the surface by that offset times the depth slope, so a difference taken
|
|
744
|
+
// from it tilts the normal by a fixed amount for a resting particle and the push slid it
|
|
745
|
+
// sideways every frame (0.13 units/s on the fx3 slab, read back by ws/fx-6). Per axis the
|
|
746
|
+
// shorter one-sided difference, so a silhouette neighbour does not bend it.
|
|
747
|
+
let c = scene_at(px, dims);
|
|
748
|
+
let rx = scene_at(px + vec2i(1, 0), dims) - c;
|
|
749
|
+
let lx = c - scene_at(px - vec2i(1, 0), dims);
|
|
750
|
+
let ry = scene_at(px + vec2i(0, 1), dims) - c;
|
|
751
|
+
let ly = c - scene_at(px - vec2i(0, 1), dims);
|
|
752
|
+
let sx = select(lx, rx, dot(rx, rx) < dot(lx, lx));
|
|
753
|
+
let sy = select(ly, ry, dot(ry, ry) < dot(ly, ly));
|
|
754
|
+
var n = cross(sx, sy);
|
|
755
|
+
if (dot(n, n) < 1e-12) { n = -ray; } else { n = normalize(n); }
|
|
756
|
+
if (dot(n, ray) > 0.0) { n = -n; }
|
|
757
|
+
// Out along the surface normal onto the plane through the hit, not back along the view ray:
|
|
758
|
+
// a ray push moves a resting particle toward the camera by its sink depth over tan(grazing
|
|
759
|
+
// angle) every frame, and it creeps off the front edge. dot(hit - pos, n) >= 0 and is at
|
|
760
|
+
// most the depth behind the surface, so a noisy normal cannot throw the particle far.
|
|
761
|
+
var put = p.pos.xyz + n * (max(dot(hit - p.pos.xyz, n), 0.0) + 1e-3);
|
|
762
|
+
if (length(p.pos.xyz - hit) > col.params.z) {
|
|
763
|
+
// Deeper than thickness: a hit only if one step back it was in front of the scene.
|
|
764
|
+
let prev = p.pos.xyz - p.vel.xyz * col.params.w;
|
|
765
|
+
let pc = col.view_proj * vec4f(prev, 1.0);
|
|
766
|
+
if (pc.w <= 1e-5) { return; }
|
|
767
|
+
let pn = pc.xyz / pc.w;
|
|
768
|
+
if (any(abs(pn.xy) > vec2f(1.0)) || pn.z < 0.0 || pn.z > 1.0) { return; }
|
|
769
|
+
let ppx = vec2i(col.viewport.xy + (pn.xy * vec2f(0.5, -0.5) + 0.5) * col.viewport.zw);
|
|
770
|
+
if (pn.z > textureLoad(scene_depth, clamp(ppx, vec2i(0), dims - 1), 0)) { return; }
|
|
771
|
+
put = prev;
|
|
772
|
+
}
|
|
773
|
+
let v = p.vel.xyz;
|
|
774
|
+
let vn = dot(v, n);
|
|
775
|
+
var nv = v;
|
|
776
|
+
if (vn < 0.0) {
|
|
777
|
+
nv = (v - n * vn) * (1.0 - col.params.y) - n * vn * col.params.x;
|
|
778
|
+
}
|
|
779
|
+
p.pos = vec4f(put, p.pos.w);
|
|
780
|
+
p.vel = vec4f(nv, p.vel.w);
|
|
781
|
+
particles[i] = p;
|
|
782
|
+
}
|
|
783
|
+
`;
|
|
784
|
+
/** Copies a depth texture into the particles' own depth32float copy (soft-particle source). */
|
|
785
|
+
export const PARTICLE_DEPTH_COPY_WGSL = /* wgsl */ `
|
|
786
|
+
@group(0) @binding(0) var src: texture_depth_2d;
|
|
787
|
+
@vertex fn vs(@builtin(vertex_index) i: u32) -> @builtin(position) vec4f {
|
|
788
|
+
let p = vec2f(f32((i << 1u) & 2u), f32(i & 2u));
|
|
789
|
+
return vec4f(p * 2.0 - 1.0, 0.0, 1.0);
|
|
790
|
+
}
|
|
791
|
+
@fragment fn fs(@builtin(position) pos: vec4f) -> @builtin(frag_depth) f32 {
|
|
792
|
+
return textureLoad(src, vec2i(pos.xy), 0);
|
|
793
|
+
}
|
|
794
|
+
`;
|
|
795
|
+
/**
|
|
796
|
+
* Counts live particles of either layout: \`stride\` vec4s per particle, \`life\` at vec4 index
|
|
797
|
+
* \`lifeAt\` (age in x, lifetime in y), and among them those whose y (vec4 \`posAt\`) is below
|
|
798
|
+
* \`belowY\`.
|
|
799
|
+
*/
|
|
800
|
+
export const PARTICLE_COUNT_WGSL = /* wgsl */ `
|
|
801
|
+
struct CountParams {
|
|
802
|
+
capacity: u32,
|
|
803
|
+
stride: u32,
|
|
804
|
+
lifeAt: u32,
|
|
805
|
+
posAt: u32,
|
|
806
|
+
belowY: f32,
|
|
807
|
+
_a: f32,
|
|
808
|
+
_b: f32,
|
|
809
|
+
_c: f32,
|
|
810
|
+
};
|
|
811
|
+
struct Counts {
|
|
812
|
+
alive: atomic<u32>,
|
|
813
|
+
below: atomic<u32>,
|
|
814
|
+
};
|
|
815
|
+
@group(0) @binding(0) var<storage, read> data: array<vec4f>;
|
|
816
|
+
@group(0) @binding(1) var<storage, read_write> counts: Counts;
|
|
817
|
+
@group(0) @binding(2) var<uniform> params: CountParams;
|
|
818
|
+
@compute @workgroup_size(64) fn count(@builtin(global_invocation_id) gid: vec3u) {
|
|
819
|
+
if (gid.x >= params.capacity) { return; }
|
|
820
|
+
let life = data[gid.x * params.stride + params.lifeAt];
|
|
821
|
+
if (life.x < life.y) {
|
|
822
|
+
atomicAdd(&counts.alive, 1u);
|
|
823
|
+
if (data[gid.x * params.stride + params.posAt].y < params.belowY) {
|
|
824
|
+
atomicAdd(&counts.below, 1u);
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
`;
|