@codexo/exojs 0.7.13 → 0.8.2
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 +637 -124
- package/README.md +19 -19
- package/dist/esm/animation/Easing.js +13 -21
- package/dist/esm/animation/Easing.js.map +1 -1
- package/dist/esm/animation/Tween.d.ts +3 -3
- package/dist/esm/animation/Tween.js +7 -5
- package/dist/esm/animation/Tween.js.map +1 -1
- package/dist/esm/animation/TweenManager.js +1 -1
- package/dist/esm/animation/TweenManager.js.map +1 -1
- package/dist/esm/animation/types.js.map +1 -1
- package/dist/esm/audio/AbstractMedia.d.ts +1 -1
- package/dist/esm/audio/AbstractMedia.js +1 -1
- package/dist/esm/audio/AbstractMedia.js.map +1 -1
- package/dist/esm/audio/AudioAnalyser.d.ts +1 -1
- package/dist/esm/audio/AudioAnalyser.js +2 -2
- package/dist/esm/audio/AudioAnalyser.js.map +1 -1
- package/dist/esm/audio/AudioBus.d.ts +1 -1
- package/dist/esm/audio/AudioBus.js.map +1 -1
- package/dist/esm/audio/AudioFilter.js +3 -1
- package/dist/esm/audio/AudioFilter.js.map +1 -1
- package/dist/esm/audio/AudioListener.d.ts +1 -1
- package/dist/esm/audio/AudioListener.js +5 -3
- package/dist/esm/audio/AudioListener.js.map +1 -1
- package/dist/esm/audio/AudioManager.js.map +1 -1
- package/dist/esm/audio/BeatDetector.d.ts +4 -4
- package/dist/esm/audio/BeatDetector.js +68 -44
- package/dist/esm/audio/BeatDetector.js.map +1 -1
- package/dist/esm/audio/Envelope.d.ts +1 -1
- package/dist/esm/audio/Envelope.js +2 -2
- package/dist/esm/audio/Envelope.js.map +1 -1
- package/dist/esm/audio/Music.d.ts +1 -1
- package/dist/esm/audio/Music.js +1 -1
- package/dist/esm/audio/Music.js.map +1 -1
- package/dist/esm/audio/OscillatorSound.d.ts +2 -2
- package/dist/esm/audio/OscillatorSound.js.map +1 -1
- package/dist/esm/audio/Sound.d.ts +40 -2
- package/dist/esm/audio/Sound.js +69 -9
- package/dist/esm/audio/Sound.js.map +1 -1
- package/dist/esm/audio/audio-context.js +1 -1
- package/dist/esm/audio/audio-context.js.map +1 -1
- package/dist/esm/audio/crossFade.js +1 -1
- package/dist/esm/audio/crossFade.js.map +1 -1
- package/dist/esm/audio/dsp/mel.d.ts +2 -2
- package/dist/esm/audio/dsp/tempogram.d.ts +2 -2
- package/dist/esm/audio/filters/ChorusFilter.js +12 -4
- package/dist/esm/audio/filters/ChorusFilter.js.map +1 -1
- package/dist/esm/audio/filters/CompressorFilter.d.ts +9 -0
- package/dist/esm/audio/filters/CompressorFilter.js +11 -0
- package/dist/esm/audio/filters/CompressorFilter.js.map +1 -1
- package/dist/esm/audio/filters/DelayFilter.js.map +1 -1
- package/dist/esm/audio/filters/DuckingFilter.d.ts +1 -1
- package/dist/esm/audio/filters/DuckingFilter.js +13 -5
- package/dist/esm/audio/filters/DuckingFilter.js.map +1 -1
- package/dist/esm/audio/filters/EqualizerFilter.js.map +1 -1
- package/dist/esm/audio/filters/GranularFilter.js +30 -14
- package/dist/esm/audio/filters/GranularFilter.js.map +1 -1
- package/dist/esm/audio/filters/HighpassFilter.js.map +1 -1
- package/dist/esm/audio/filters/LowpassFilter.js.map +1 -1
- package/dist/esm/audio/filters/PitchShiftFilter.js +14 -10
- package/dist/esm/audio/filters/PitchShiftFilter.js.map +1 -1
- package/dist/esm/audio/filters/ReverbFilter.js.map +1 -1
- package/dist/esm/audio/filters/VocoderFilter.d.ts +2 -2
- package/dist/esm/audio/filters/VocoderFilter.js +20 -12
- package/dist/esm/audio/filters/VocoderFilter.js.map +1 -1
- package/dist/esm/audio/filters/WorkletFilter.js +5 -6
- package/dist/esm/audio/filters/WorkletFilter.js.map +1 -1
- package/dist/esm/audio/filters/index.d.ts +7 -7
- package/dist/esm/audio/index.d.ts +9 -9
- package/dist/esm/audio/worklet/registerWorklet.js +7 -3
- package/dist/esm/audio/worklet/registerWorklet.js.map +1 -1
- package/dist/esm/core/Application.d.ts +13 -13
- package/dist/esm/core/Application.js +24 -16
- package/dist/esm/core/Application.js.map +1 -1
- package/dist/esm/core/Bounds.d.ts +1 -1
- package/dist/esm/core/Bounds.js +1 -3
- package/dist/esm/core/Bounds.js.map +1 -1
- package/dist/esm/core/Clock.js +1 -1
- package/dist/esm/core/Clock.js.map +1 -1
- package/dist/esm/core/Color.js +2 -5
- package/dist/esm/core/Color.js.map +1 -1
- package/dist/esm/core/Scene.d.ts +10 -10
- package/dist/esm/core/Scene.js +1 -1
- package/dist/esm/core/Scene.js.map +1 -1
- package/dist/esm/core/SceneManager.d.ts +4 -4
- package/dist/esm/core/SceneManager.js +39 -33
- package/dist/esm/core/SceneManager.js.map +1 -1
- package/dist/esm/core/SceneNode.d.ts +8 -8
- package/dist/esm/core/SceneNode.js +39 -29
- package/dist/esm/core/SceneNode.js.map +1 -1
- package/dist/esm/core/Signal.d.ts +3 -3
- package/dist/esm/core/Signal.js +3 -3
- package/dist/esm/core/Signal.js.map +1 -1
- package/dist/esm/core/Time.js +6 -6
- package/dist/esm/core/Time.js.map +1 -1
- package/dist/esm/core/Timer.d.ts +1 -1
- package/dist/esm/core/Timer.js +1 -1
- package/dist/esm/core/Timer.js.map +1 -1
- package/dist/esm/core/capabilities.js +2 -4
- package/dist/esm/core/capabilities.js.map +1 -1
- package/dist/esm/core/index.d.ts +2 -2
- package/dist/esm/core/utils.d.ts +4 -4
- package/dist/esm/core/utils.js +10 -10
- package/dist/esm/core/utils.js.map +1 -1
- package/dist/esm/debug/BoundingBoxesLayer.d.ts +3 -3
- package/dist/esm/debug/BoundingBoxesLayer.js +6 -13
- package/dist/esm/debug/BoundingBoxesLayer.js.map +1 -1
- package/dist/esm/debug/DebugLayer.d.ts +1 -1
- package/dist/esm/debug/DebugLayer.js.map +1 -1
- package/dist/esm/debug/DebugOverlay.d.ts +2 -2
- package/dist/esm/debug/DebugOverlay.js +2 -2
- package/dist/esm/debug/DebugOverlay.js.map +1 -1
- package/dist/esm/debug/HitTestLayer.d.ts +3 -3
- package/dist/esm/debug/HitTestLayer.js +3 -10
- package/dist/esm/debug/HitTestLayer.js.map +1 -1
- package/dist/esm/debug/PerformanceLayer.d.ts +2 -2
- package/dist/esm/debug/PerformanceLayer.js +6 -6
- package/dist/esm/debug/PerformanceLayer.js.map +1 -1
- package/dist/esm/debug/PointerStackLayer.d.ts +3 -3
- package/dist/esm/debug/PointerStackLayer.js +3 -3
- package/dist/esm/debug/PointerStackLayer.js.map +1 -1
- package/dist/esm/debug/index.d.ts +3 -3
- package/dist/esm/debug/index.js +3 -3
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +106 -84
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/input/ArcadeStickGamepadMapping.js.map +1 -1
- package/dist/esm/input/GameCubeGamepadMapping.d.ts +1 -1
- package/dist/esm/input/GameCubeGamepadMapping.js +1 -1
- package/dist/esm/input/GameCubeGamepadMapping.js.map +1 -1
- package/dist/esm/input/Gamepad.d.ts +7 -7
- package/dist/esm/input/Gamepad.js +10 -6
- package/dist/esm/input/Gamepad.js.map +1 -1
- package/dist/esm/input/GamepadAxis.js +1 -1
- package/dist/esm/input/GamepadAxis.js.map +1 -1
- package/dist/esm/input/GamepadButton.js +1 -1
- package/dist/esm/input/GamepadButton.js.map +1 -1
- package/dist/esm/input/GamepadDefinitions.d.ts +4 -4
- package/dist/esm/input/GamepadDefinitions.js +12 -14
- package/dist/esm/input/GamepadDefinitions.js.map +1 -1
- package/dist/esm/input/GamepadMapping.d.ts +5 -5
- package/dist/esm/input/GamepadMapping.js.map +1 -1
- package/dist/esm/input/GamepadPromptLayouts.d.ts +2 -2
- package/dist/esm/input/GamepadPromptLayouts.js +8 -8
- package/dist/esm/input/GamepadPromptLayouts.js.map +1 -1
- package/dist/esm/input/GenericDualAnalogGamepadMapping.js.map +1 -1
- package/dist/esm/input/GestureRecognizer.d.ts +1 -1
- package/dist/esm/input/GestureRecognizer.js.map +1 -1
- package/dist/esm/input/InputBinding.d.ts +3 -3
- package/dist/esm/input/InputBinding.js.map +1 -1
- package/dist/esm/input/InputManager.d.ts +9 -9
- package/dist/esm/input/InputManager.js +25 -16
- package/dist/esm/input/InputManager.js.map +1 -1
- package/dist/esm/input/InteractionEvent.d.ts +1 -1
- package/dist/esm/input/InteractionEvent.js.map +1 -1
- package/dist/esm/input/InteractionManager.d.ts +2 -2
- package/dist/esm/input/InteractionManager.js +25 -16
- package/dist/esm/input/InteractionManager.js.map +1 -1
- package/dist/esm/input/JoyConLeftGamepadMapping.js.map +1 -1
- package/dist/esm/input/JoyConRightGamepadMapping.js.map +1 -1
- package/dist/esm/input/PlayStationGamepadMapping.d.ts +1 -1
- package/dist/esm/input/PlayStationGamepadMapping.js +1 -1
- package/dist/esm/input/PlayStationGamepadMapping.js.map +1 -1
- package/dist/esm/input/Pointer.d.ts +2 -2
- package/dist/esm/input/Pointer.js +6 -6
- package/dist/esm/input/Pointer.js.map +1 -1
- package/dist/esm/input/SteamControllerGamepadMapping.d.ts +1 -1
- package/dist/esm/input/SteamControllerGamepadMapping.js +1 -1
- package/dist/esm/input/SteamControllerGamepadMapping.js.map +1 -1
- package/dist/esm/input/SteamDeckGamepadMapping.js.map +1 -1
- package/dist/esm/input/SwitchProGamepadMapping.d.ts +1 -1
- package/dist/esm/input/SwitchProGamepadMapping.js +1 -1
- package/dist/esm/input/SwitchProGamepadMapping.js.map +1 -1
- package/dist/esm/input/XboxGamepadMapping.d.ts +1 -1
- package/dist/esm/input/XboxGamepadMapping.js +1 -1
- package/dist/esm/input/XboxGamepadMapping.js.map +1 -1
- package/dist/esm/input/index.d.ts +16 -16
- package/dist/esm/input/interaction-hooks.js.map +1 -1
- package/dist/esm/input/types.js.map +1 -1
- package/dist/esm/math/AbstractVector.js +8 -9
- package/dist/esm/math/AbstractVector.js.map +1 -1
- package/dist/esm/math/Circle.d.ts +5 -5
- package/dist/esm/math/Circle.js +33 -21
- package/dist/esm/math/Circle.js.map +1 -1
- package/dist/esm/math/Collision.d.ts +2 -2
- package/dist/esm/math/Collision.js.map +1 -1
- package/dist/esm/math/Ellipse.d.ts +5 -5
- package/dist/esm/math/Ellipse.js +28 -19
- package/dist/esm/math/Ellipse.js.map +1 -1
- package/dist/esm/math/Flags.d.ts +4 -4
- package/dist/esm/math/Flags.js.map +1 -1
- package/dist/esm/math/Interval.js.map +1 -1
- package/dist/esm/math/Line.d.ts +5 -5
- package/dist/esm/math/Line.js +23 -15
- package/dist/esm/math/Line.js.map +1 -1
- package/dist/esm/math/Matrix.js +14 -16
- package/dist/esm/math/Matrix.js.map +1 -1
- package/dist/esm/math/ObservableSize.js.map +1 -1
- package/dist/esm/math/ObservableVector.d.ts +1 -1
- package/dist/esm/math/ObservableVector.js.map +1 -1
- package/dist/esm/math/PolarVector.js.map +1 -1
- package/dist/esm/math/Polygon.d.ts +11 -11
- package/dist/esm/math/Polygon.js +37 -26
- package/dist/esm/math/Polygon.js.map +1 -1
- package/dist/esm/math/PolygonLike.d.ts +1 -1
- package/dist/esm/math/Quadtree.js +2 -8
- package/dist/esm/math/Quadtree.js.map +1 -1
- package/dist/esm/math/Random.js +5 -5
- package/dist/esm/math/Random.js.map +1 -1
- package/dist/esm/math/Rectangle.d.ts +4 -4
- package/dist/esm/math/Rectangle.js +59 -41
- package/dist/esm/math/Rectangle.js.map +1 -1
- package/dist/esm/math/Segment.d.ts +1 -1
- package/dist/esm/math/Segment.js +4 -4
- package/dist/esm/math/Segment.js.map +1 -1
- package/dist/esm/math/ShapeLike.d.ts +1 -1
- package/dist/esm/math/Size.js +1 -2
- package/dist/esm/math/Size.js.map +1 -1
- package/dist/esm/math/Vector.d.ts +4 -4
- package/dist/esm/math/Vector.js +19 -13
- package/dist/esm/math/Vector.js.map +1 -1
- package/dist/esm/math/collision-detection.d.ts +3 -3
- package/dist/esm/math/collision-detection.js +49 -49
- package/dist/esm/math/collision-detection.js.map +1 -1
- package/dist/esm/math/collision-primitives.d.ts +8 -8
- package/dist/esm/math/collision-primitives.js +27 -33
- package/dist/esm/math/collision-primitives.js.map +1 -1
- package/dist/esm/math/geometry.d.ts +3 -3
- package/dist/esm/math/geometry.js +55 -48
- package/dist/esm/math/geometry.js.map +1 -1
- package/dist/esm/math/index.d.ts +16 -16
- package/dist/esm/math/swept-collision.d.ts +3 -3
- package/dist/esm/math/swept-collision.js +5 -8
- package/dist/esm/math/swept-collision.js.map +1 -1
- package/dist/esm/math/triangulate.js +34 -24
- package/dist/esm/math/triangulate.js.map +1 -1
- package/dist/esm/math/utils.d.ts +2 -2
- package/dist/esm/math/utils.js +7 -7
- package/dist/esm/math/utils.js.map +1 -1
- package/dist/esm/particles/ParticleSystem.d.ts +181 -84
- package/dist/esm/particles/ParticleSystem.js +460 -147
- package/dist/esm/particles/ParticleSystem.js.map +1 -1
- package/dist/esm/particles/distributions/BoxArea.d.ts +17 -0
- package/dist/esm/particles/distributions/BoxArea.js +48 -0
- package/dist/esm/particles/distributions/BoxArea.js.map +1 -0
- package/dist/esm/particles/distributions/CircleArea.d.ts +19 -0
- package/dist/esm/particles/distributions/CircleArea.js +33 -0
- package/dist/esm/particles/distributions/CircleArea.js.map +1 -0
- package/dist/esm/particles/distributions/ConeDirection.d.ts +28 -0
- package/dist/esm/particles/distributions/ConeDirection.js +44 -0
- package/dist/esm/particles/distributions/ConeDirection.js.map +1 -0
- package/dist/esm/particles/distributions/Constant.d.ts +17 -0
- package/dist/esm/particles/distributions/Constant.js +35 -0
- package/dist/esm/particles/distributions/Constant.js.map +1 -0
- package/dist/esm/particles/distributions/Curve.d.ts +30 -0
- package/dist/esm/particles/distributions/Curve.js +53 -0
- package/dist/esm/particles/distributions/Curve.js.map +1 -0
- package/dist/esm/particles/distributions/Distribution.d.ts +45 -0
- package/dist/esm/particles/distributions/Gradient.d.ts +40 -0
- package/dist/esm/particles/distributions/Gradient.js +72 -0
- package/dist/esm/particles/distributions/Gradient.js.map +1 -0
- package/dist/esm/particles/distributions/LineSegment.d.ts +15 -0
- package/dist/esm/particles/distributions/LineSegment.js +27 -0
- package/dist/esm/particles/distributions/LineSegment.js.map +1 -0
- package/dist/esm/particles/distributions/Range.d.ts +12 -0
- package/dist/esm/particles/distributions/Range.js +19 -0
- package/dist/esm/particles/distributions/Range.js.map +1 -0
- package/dist/esm/particles/distributions/VectorRange.d.ts +20 -0
- package/dist/esm/particles/distributions/VectorRange.js +31 -0
- package/dist/esm/particles/distributions/VectorRange.js.map +1 -0
- package/dist/esm/particles/distributions/index.d.ts +12 -0
- package/dist/esm/particles/gpu/ParticleGpuState.d.ts +57 -0
- package/dist/esm/particles/gpu/ParticleGpuState.js +535 -0
- package/dist/esm/particles/gpu/ParticleGpuState.js.map +1 -0
- package/dist/esm/particles/index.d.ts +2 -10
- package/dist/esm/particles/modules/AlphaFadeOverLifetime.d.ts +24 -0
- package/dist/esm/particles/modules/AlphaFadeOverLifetime.js +60 -0
- package/dist/esm/particles/modules/AlphaFadeOverLifetime.js.map +1 -0
- package/dist/esm/particles/modules/ApplyForce.d.ts +20 -0
- package/dist/esm/particles/modules/ApplyForce.js +48 -0
- package/dist/esm/particles/modules/ApplyForce.js.map +1 -0
- package/dist/esm/particles/modules/AttractToPoint.d.ts +27 -0
- package/dist/esm/particles/modules/AttractToPoint.js +73 -0
- package/dist/esm/particles/modules/AttractToPoint.js.map +1 -0
- package/dist/esm/particles/modules/BurstSpawn.d.ts +53 -0
- package/dist/esm/particles/modules/BurstSpawn.js +94 -0
- package/dist/esm/particles/modules/BurstSpawn.js.map +1 -0
- package/dist/esm/particles/modules/ColorOverLifetime.d.ts +22 -0
- package/dist/esm/particles/modules/ColorOverLifetime.js +65 -0
- package/dist/esm/particles/modules/ColorOverLifetime.js.map +1 -0
- package/dist/esm/particles/modules/ColorOverSpeed.d.ts +27 -0
- package/dist/esm/particles/modules/ColorOverSpeed.js +86 -0
- package/dist/esm/particles/modules/ColorOverSpeed.js.map +1 -0
- package/dist/esm/particles/modules/DeathModule.d.ts +24 -0
- package/dist/esm/particles/modules/DeathModule.js +25 -0
- package/dist/esm/particles/modules/DeathModule.js.map +1 -0
- package/dist/esm/particles/modules/Drag.d.ts +20 -0
- package/dist/esm/particles/modules/Drag.js +43 -0
- package/dist/esm/particles/modules/Drag.js.map +1 -0
- package/dist/esm/particles/modules/OrbitalForce.d.ts +28 -0
- package/dist/esm/particles/modules/OrbitalForce.js +65 -0
- package/dist/esm/particles/modules/OrbitalForce.js.map +1 -0
- package/dist/esm/particles/modules/RateSpawn.d.ts +41 -0
- package/dist/esm/particles/modules/RateSpawn.js +76 -0
- package/dist/esm/particles/modules/RateSpawn.js.map +1 -0
- package/dist/esm/particles/modules/RepelFromPoint.d.ts +24 -0
- package/dist/esm/particles/modules/RepelFromPoint.js +76 -0
- package/dist/esm/particles/modules/RepelFromPoint.js.map +1 -0
- package/dist/esm/particles/modules/RotateOverLifetime.d.ts +20 -0
- package/dist/esm/particles/modules/RotateOverLifetime.js +41 -0
- package/dist/esm/particles/modules/RotateOverLifetime.js.map +1 -0
- package/dist/esm/particles/modules/ScaleOverLifetime.d.ts +26 -0
- package/dist/esm/particles/modules/ScaleOverLifetime.js +59 -0
- package/dist/esm/particles/modules/ScaleOverLifetime.js.map +1 -0
- package/dist/esm/particles/modules/SpawnModule.d.ts +30 -0
- package/dist/esm/particles/modules/SpawnModule.js +31 -0
- package/dist/esm/particles/modules/SpawnModule.js.map +1 -0
- package/dist/esm/particles/modules/SpawnOnDeath.d.ts +24 -0
- package/dist/esm/particles/modules/SpawnOnDeath.js +47 -0
- package/dist/esm/particles/modules/SpawnOnDeath.js.map +1 -0
- package/dist/esm/particles/modules/Turbulence.d.ts +30 -0
- package/dist/esm/particles/modules/Turbulence.js +122 -0
- package/dist/esm/particles/modules/Turbulence.js.map +1 -0
- package/dist/esm/particles/modules/UpdateModule.d.ts +95 -0
- package/dist/esm/particles/modules/UpdateModule.js +66 -0
- package/dist/esm/particles/modules/UpdateModule.js.map +1 -0
- package/dist/esm/particles/modules/VelocityOverLifetime.d.ts +30 -0
- package/dist/esm/particles/modules/VelocityOverLifetime.js +84 -0
- package/dist/esm/particles/modules/VelocityOverLifetime.js.map +1 -0
- package/dist/esm/particles/modules/WgslContribution.d.ts +81 -0
- package/dist/esm/particles/modules/WgslContribution.js +34 -0
- package/dist/esm/particles/modules/WgslContribution.js.map +1 -0
- package/dist/esm/particles/modules/index.d.ts +22 -0
- package/dist/esm/rendering/CallbackRenderPass.js.map +1 -1
- package/dist/esm/rendering/Container.d.ts +2 -2
- package/dist/esm/rendering/Container.js +10 -11
- package/dist/esm/rendering/Container.js.map +1 -1
- package/dist/esm/rendering/Drawable.js.map +1 -1
- package/dist/esm/rendering/RenderBackend.d.ts +6 -6
- package/dist/esm/rendering/RenderBackendType.js.map +1 -1
- package/dist/esm/rendering/RenderNode.d.ts +10 -10
- package/dist/esm/rendering/RenderNode.js +31 -31
- package/dist/esm/rendering/RenderNode.js.map +1 -1
- package/dist/esm/rendering/RenderStats.js.map +1 -1
- package/dist/esm/rendering/RenderTarget.d.ts +2 -2
- package/dist/esm/rendering/RenderTarget.js +5 -5
- package/dist/esm/rendering/RenderTarget.js.map +1 -1
- package/dist/esm/rendering/RenderTargetPass.d.ts +3 -3
- package/dist/esm/rendering/RenderTargetPass.js.map +1 -1
- package/dist/esm/rendering/Renderer.d.ts +2 -2
- package/dist/esm/rendering/RendererRegistry.d.ts +3 -3
- package/dist/esm/rendering/RendererRegistry.js +2 -3
- package/dist/esm/rendering/RendererRegistry.js.map +1 -1
- package/dist/esm/rendering/View.d.ts +3 -3
- package/dist/esm/rendering/View.js +17 -20
- package/dist/esm/rendering/View.js.map +1 -1
- package/dist/esm/rendering/filters/BlurFilter.d.ts +1 -1
- package/dist/esm/rendering/filters/BlurFilter.js +4 -9
- package/dist/esm/rendering/filters/BlurFilter.js.map +1 -1
- package/dist/esm/rendering/filters/ColorFilter.d.ts +1 -1
- package/dist/esm/rendering/filters/ColorFilter.js +3 -9
- package/dist/esm/rendering/filters/ColorFilter.js.map +1 -1
- package/dist/esm/rendering/filters/Filter.d.ts +1 -1
- package/dist/esm/rendering/filters/Filter.js.map +1 -1
- package/dist/esm/rendering/filters/LutFilter.d.ts +87 -0
- package/dist/esm/rendering/filters/LutFilter.js +261 -0
- package/dist/esm/rendering/filters/LutFilter.js.map +1 -0
- package/dist/esm/rendering/filters/WebGl2ShaderFilter.d.ts +2 -2
- package/dist/esm/rendering/filters/WebGl2ShaderFilter.js +7 -14
- package/dist/esm/rendering/filters/WebGl2ShaderFilter.js.map +1 -1
- package/dist/esm/rendering/filters/WebGpuShaderFilter.d.ts +1 -1
- package/dist/esm/rendering/filters/WebGpuShaderFilter.js +11 -10
- package/dist/esm/rendering/filters/WebGpuShaderFilter.js.map +1 -1
- package/dist/esm/rendering/index.d.ts +28 -27
- package/dist/esm/rendering/mesh/Mesh.d.ts +49 -1
- package/dist/esm/rendering/mesh/Mesh.js +3 -1
- package/dist/esm/rendering/mesh/Mesh.js.map +1 -1
- package/dist/esm/rendering/primitives/Graphics.d.ts +3 -3
- package/dist/esm/rendering/primitives/Graphics.js +12 -12
- package/dist/esm/rendering/primitives/Graphics.js.map +1 -1
- package/dist/esm/rendering/shader/Shader.js.map +1 -1
- package/dist/esm/rendering/shader/ShaderAttribute.js.map +1 -1
- package/dist/esm/rendering/shader/ShaderUniform.js.map +1 -1
- package/dist/esm/rendering/shader/upgradeFragmentShaderToGl300.js +6 -8
- package/dist/esm/rendering/shader/upgradeFragmentShaderToGl300.js.map +1 -1
- package/dist/esm/rendering/sprite/AnimatedSprite.d.ts +3 -3
- package/dist/esm/rendering/sprite/AnimatedSprite.js.map +1 -1
- package/dist/esm/rendering/sprite/Sprite.d.ts +4 -4
- package/dist/esm/rendering/sprite/Sprite.js +51 -35
- package/dist/esm/rendering/sprite/Sprite.js.map +1 -1
- package/dist/esm/rendering/sprite/Spritesheet.d.ts +5 -9
- package/dist/esm/rendering/sprite/Spritesheet.js +1 -1
- package/dist/esm/rendering/sprite/Spritesheet.js.map +1 -1
- package/dist/esm/rendering/text/DynamicGlyphAtlas.js +3 -9
- package/dist/esm/rendering/text/DynamicGlyphAtlas.js.map +1 -1
- package/dist/esm/rendering/text/Text.js +9 -9
- package/dist/esm/rendering/text/Text.js.map +1 -1
- package/dist/esm/rendering/text/TextLayout.d.ts +2 -2
- package/dist/esm/rendering/text/TextLayout.js.map +1 -1
- package/dist/esm/rendering/text/TextStyle.js.map +1 -1
- package/dist/esm/rendering/text/atlas-singleton.js.map +1 -1
- package/dist/esm/rendering/texture/RenderTexture.d.ts +1 -1
- package/dist/esm/rendering/texture/RenderTexture.js +4 -1
- package/dist/esm/rendering/texture/RenderTexture.js.map +1 -1
- package/dist/esm/rendering/texture/Sampler.js.map +1 -1
- package/dist/esm/rendering/texture/Texture.d.ts +2 -2
- package/dist/esm/rendering/texture/Texture.js +8 -5
- package/dist/esm/rendering/texture/Texture.js.map +1 -1
- package/dist/esm/rendering/types.js.map +1 -1
- package/dist/esm/rendering/utils.js.map +1 -1
- package/dist/esm/rendering/video/Video.d.ts +3 -3
- package/dist/esm/rendering/video/Video.js +7 -6
- package/dist/esm/rendering/video/Video.js.map +1 -1
- package/dist/esm/rendering/webgl2/AbstractWebGl2BatchedRenderer.d.ts +6 -6
- package/dist/esm/rendering/webgl2/AbstractWebGl2BatchedRenderer.js +7 -10
- package/dist/esm/rendering/webgl2/AbstractWebGl2BatchedRenderer.js.map +1 -1
- package/dist/esm/rendering/webgl2/AbstractWebGl2Renderer.d.ts +1 -1
- package/dist/esm/rendering/webgl2/AbstractWebGl2Renderer.js +1 -2
- package/dist/esm/rendering/webgl2/AbstractWebGl2Renderer.js.map +1 -1
- package/dist/esm/rendering/webgl2/WebGl2Backend.d.ts +11 -11
- package/dist/esm/rendering/webgl2/WebGl2Backend.js +19 -21
- package/dist/esm/rendering/webgl2/WebGl2Backend.js.map +1 -1
- package/dist/esm/rendering/webgl2/WebGl2MaskCompositor.d.ts +2 -2
- package/dist/esm/rendering/webgl2/WebGl2MaskCompositor.js +6 -8
- package/dist/esm/rendering/webgl2/WebGl2MaskCompositor.js.map +1 -1
- package/dist/esm/rendering/webgl2/WebGl2MeshRenderer.d.ts +7 -4
- package/dist/esm/rendering/webgl2/WebGl2MeshRenderer.js +110 -43
- package/dist/esm/rendering/webgl2/WebGl2MeshRenderer.js.map +1 -1
- package/dist/esm/rendering/webgl2/WebGl2ParticleRenderer.d.ts +10 -15
- package/dist/esm/rendering/webgl2/WebGl2ParticleRenderer.js +100 -79
- package/dist/esm/rendering/webgl2/WebGl2ParticleRenderer.js.map +1 -1
- package/dist/esm/rendering/webgl2/WebGl2RenderBuffer.d.ts +1 -1
- package/dist/esm/rendering/webgl2/WebGl2RenderBuffer.js.map +1 -1
- package/dist/esm/rendering/webgl2/WebGl2ShaderBlock.js.map +1 -1
- package/dist/esm/rendering/webgl2/WebGl2ShaderMappings.js.map +1 -1
- package/dist/esm/rendering/webgl2/WebGl2ShaderProgram.js +54 -22
- package/dist/esm/rendering/webgl2/WebGl2ShaderProgram.js.map +1 -1
- package/dist/esm/rendering/webgl2/WebGl2SpriteRenderer.d.ts +1 -1
- package/dist/esm/rendering/webgl2/WebGl2SpriteRenderer.js +11 -14
- package/dist/esm/rendering/webgl2/WebGl2SpriteRenderer.js.map +1 -1
- package/dist/esm/rendering/webgl2/WebGl2VertexArrayObject.d.ts +1 -1
- package/dist/esm/rendering/webgl2/WebGl2VertexArrayObject.js.map +1 -1
- package/dist/esm/rendering/webgl2/glsl/particle.vert.js +1 -1
- package/dist/esm/rendering/webgpu/AbstractWebGpuRenderer.d.ts +1 -1
- package/dist/esm/rendering/webgpu/AbstractWebGpuRenderer.js +1 -2
- package/dist/esm/rendering/webgpu/AbstractWebGpuRenderer.js.map +1 -1
- package/dist/esm/rendering/webgpu/WebGpuBackend.d.ts +7 -7
- package/dist/esm/rendering/webgpu/WebGpuBackend.js +44 -30
- package/dist/esm/rendering/webgpu/WebGpuBackend.js.map +1 -1
- package/dist/esm/rendering/webgpu/WebGpuBlendState.js.map +1 -1
- package/dist/esm/rendering/webgpu/WebGpuMaskCompositor.d.ts +2 -2
- package/dist/esm/rendering/webgpu/WebGpuMaskCompositor.js +1 -3
- package/dist/esm/rendering/webgpu/WebGpuMaskCompositor.js.map +1 -1
- package/dist/esm/rendering/webgpu/WebGpuMeshRenderer.d.ts +2 -2
- package/dist/esm/rendering/webgpu/WebGpuMeshRenderer.js +25 -23
- package/dist/esm/rendering/webgpu/WebGpuMeshRenderer.js.map +1 -1
- package/dist/esm/rendering/webgpu/WebGpuParticleRenderer.d.ts +10 -1
- package/dist/esm/rendering/webgpu/WebGpuParticleRenderer.js +204 -76
- package/dist/esm/rendering/webgpu/WebGpuParticleRenderer.js.map +1 -1
- package/dist/esm/rendering/webgpu/WebGpuSpriteRenderer.d.ts +2 -2
- package/dist/esm/rendering/webgpu/WebGpuSpriteRenderer.js +45 -45
- package/dist/esm/rendering/webgpu/WebGpuSpriteRenderer.js.map +1 -1
- package/dist/esm/rendering/webgpu/compute/WebGpuComputePipeline.d.ts +52 -0
- package/dist/esm/rendering/webgpu/compute/WebGpuStorageBuffer.d.ts +29 -0
- package/dist/esm/rendering/webgpu/compute/index.d.ts +3 -0
- package/dist/esm/resources/AbstractAssetFactory.d.ts +1 -1
- package/dist/esm/resources/AbstractAssetFactory.js.map +1 -1
- package/dist/esm/resources/AssetManifest.d.ts +1 -1
- package/dist/esm/resources/AssetManifest.js +2 -2
- package/dist/esm/resources/AssetManifest.js.map +1 -1
- package/dist/esm/resources/CacheFirstStrategy.d.ts +2 -2
- package/dist/esm/resources/CacheFirstStrategy.js.map +1 -1
- package/dist/esm/resources/CacheStrategy.d.ts +1 -1
- package/dist/esm/resources/FactoryRegistry.d.ts +1 -1
- package/dist/esm/resources/FactoryRegistry.js +2 -3
- package/dist/esm/resources/FactoryRegistry.js.map +1 -1
- package/dist/esm/resources/IndexedDbDatabase.d.ts +1 -1
- package/dist/esm/resources/IndexedDbDatabase.js +11 -14
- package/dist/esm/resources/IndexedDbDatabase.js.map +1 -1
- package/dist/esm/resources/IndexedDbStore.d.ts +1 -1
- package/dist/esm/resources/IndexedDbStore.js +2 -7
- package/dist/esm/resources/IndexedDbStore.js.map +1 -1
- package/dist/esm/resources/Loader.d.ts +9 -9
- package/dist/esm/resources/Loader.js +48 -35
- package/dist/esm/resources/Loader.js.map +1 -1
- package/dist/esm/resources/NetworkOnlyStrategy.d.ts +2 -2
- package/dist/esm/resources/NetworkOnlyStrategy.js.map +1 -1
- package/dist/esm/resources/factories/BinaryFactory.js.map +1 -1
- package/dist/esm/resources/factories/FontFactory.js +1 -1
- package/dist/esm/resources/factories/FontFactory.js.map +1 -1
- package/dist/esm/resources/factories/ImageFactory.js +4 -4
- package/dist/esm/resources/factories/ImageFactory.js.map +1 -1
- package/dist/esm/resources/factories/JsonFactory.d.ts +1 -1
- package/dist/esm/resources/factories/JsonFactory.js +1 -1
- package/dist/esm/resources/factories/JsonFactory.js.map +1 -1
- package/dist/esm/resources/factories/MusicFactory.d.ts +1 -1
- package/dist/esm/resources/factories/MusicFactory.js +4 -4
- package/dist/esm/resources/factories/MusicFactory.js.map +1 -1
- package/dist/esm/resources/factories/SoundFactory.d.ts +1 -1
- package/dist/esm/resources/factories/SoundFactory.js +3 -3
- package/dist/esm/resources/factories/SoundFactory.js.map +1 -1
- package/dist/esm/resources/factories/SvgFactory.js +3 -3
- package/dist/esm/resources/factories/SvgFactory.js.map +1 -1
- package/dist/esm/resources/factories/TextFactory.js +1 -1
- package/dist/esm/resources/factories/TextFactory.js.map +1 -1
- package/dist/esm/resources/factories/TextureFactory.d.ts +1 -1
- package/dist/esm/resources/factories/TextureFactory.js +3 -3
- package/dist/esm/resources/factories/TextureFactory.js.map +1 -1
- package/dist/esm/resources/factories/VideoFactory.d.ts +2 -2
- package/dist/esm/resources/factories/VideoFactory.js +5 -5
- package/dist/esm/resources/factories/VideoFactory.js.map +1 -1
- package/dist/esm/resources/factories/VttFactory.d.ts +2 -2
- package/dist/esm/resources/factories/VttFactory.js +6 -6
- package/dist/esm/resources/factories/VttFactory.js.map +1 -1
- package/dist/esm/resources/factories/WasmFactory.js.map +1 -1
- package/dist/esm/resources/index.d.ts +11 -11
- package/dist/esm/resources/utils.js +30 -25
- package/dist/esm/resources/utils.js.map +1 -1
- package/dist/exo.esm.js +26438 -23869
- package/dist/exo.esm.js.map +1 -1
- package/package.json +15 -4
- package/dist/esm/particles/Particle.d.ts +0 -77
- package/dist/esm/particles/Particle.js +0 -143
- package/dist/esm/particles/Particle.js.map +0 -1
- package/dist/esm/particles/ParticleProperties.d.ts +0 -29
- package/dist/esm/particles/affectors/ColorAffector.d.ts +0 -30
- package/dist/esm/particles/affectors/ColorAffector.js +0 -55
- package/dist/esm/particles/affectors/ColorAffector.js.map +0 -1
- package/dist/esm/particles/affectors/ForceAffector.d.ts +0 -24
- package/dist/esm/particles/affectors/ForceAffector.js +0 -39
- package/dist/esm/particles/affectors/ForceAffector.js.map +0 -1
- package/dist/esm/particles/affectors/ParticleAffector.d.ts +0 -19
- package/dist/esm/particles/affectors/ScaleAffector.d.ts +0 -23
- package/dist/esm/particles/affectors/ScaleAffector.js +0 -38
- package/dist/esm/particles/affectors/ScaleAffector.js.map +0 -1
- package/dist/esm/particles/affectors/TorqueAffector.d.ts +0 -23
- package/dist/esm/particles/affectors/TorqueAffector.js +0 -37
- package/dist/esm/particles/affectors/TorqueAffector.js.map +0 -1
- package/dist/esm/particles/emitters/ParticleEmitter.d.ts +0 -19
- package/dist/esm/particles/emitters/ParticleOptions.d.ts +0 -62
- package/dist/esm/particles/emitters/ParticleOptions.js +0 -120
- package/dist/esm/particles/emitters/ParticleOptions.js.map +0 -1
- package/dist/esm/particles/emitters/UniversalEmitter.d.ts +0 -40
- package/dist/esm/particles/emitters/UniversalEmitter.js +0 -68
- package/dist/esm/particles/emitters/UniversalEmitter.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,507 @@ All notable changes to ExoJS are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [0.8.2] - 2026-05-09
|
|
8
|
+
|
|
9
|
+
### Engine
|
|
10
|
+
|
|
11
|
+
- **`Mesh` accepts custom WebGL2 shaders.** New `MeshShaderConfig` + `MeshShaderUniformValue`
|
|
12
|
+
exports. Supply `shader: { vertexSource, fragmentSource, uniforms }` in `MeshOptions` to
|
|
13
|
+
bind a custom GLSL ES 3.00 program against the standard mesh vertex layout. Auto-bound
|
|
14
|
+
uniforms (`u_projection`, `u_translation`, `u_tint`, `u_texture`) are set only when the
|
|
15
|
+
shader declares them, so Shadertoy-style fullscreen passes can ignore them entirely.
|
|
16
|
+
Texture uniforms claim slots 1–7. WebGL2 only in this release; the WebGPU mesh
|
|
17
|
+
renderer throws a clear error pointing to the WebGL2 backend if `mesh.shader` is set.
|
|
18
|
+
|
|
19
|
+
- **Filter chain memory: ping-pong RT reuse.** `RenderNode._renderContentToTexture` now
|
|
20
|
+
releases the previous step's RenderTexture immediately after each `filter.apply`, so
|
|
21
|
+
the pool can hand the same memory back to the next step. Multi-filter chains drop from
|
|
22
|
+
N+1 simultaneously-allocated RTs to a steady-state of 2. ~60% RT-memory reduction on
|
|
23
|
+
4-filter 1080p chains. Behaviour-identical; no public API change.
|
|
24
|
+
|
|
25
|
+
### Site / Docs
|
|
26
|
+
|
|
27
|
+
- Part 2 "Core Concepts" guide section published (6 chapters, source-verified):
|
|
28
|
+
Application, Scenes, Scene lifecycle, Scene graph, Coordinates and views,
|
|
29
|
+
Loading and resources.
|
|
30
|
+
- Astro `6.3.0 → 6.3.1`, `@types/node 25.6.0 → 25.6.2` in site/.
|
|
31
|
+
|
|
32
|
+
### Verification
|
|
33
|
+
|
|
34
|
+
- Engine: 100/100 suites, 1266/1266 tests, lint:strict 0/0, typecheck clean.
|
|
35
|
+
- Site: build green (488 pages), check-ts 0/0, screenshot smoke 36/36.
|
|
36
|
+
|
|
37
|
+
## [0.8.1] - 2026-05-08
|
|
38
|
+
|
|
39
|
+
Three small additive features that close the remaining examples-driven API gaps from
|
|
40
|
+
the 0.8.0 audit, plus a long-overdue lint/format tooling consolidation and a 19-chapter
|
|
41
|
+
examples reorganisation.
|
|
42
|
+
|
|
43
|
+
### Added
|
|
44
|
+
|
|
45
|
+
- **`Sound` spatial falloff configuration.** `DistanceModel` type (`'linear' | 'inverse'
|
|
46
|
+
| 'exponential'`), plus optional `distanceModel`, `refDistance`, `maxDistance`, and
|
|
47
|
+
`rolloffFactor` fields on `SoundOptions`. The four are also exposed as live property
|
|
48
|
+
setters that lazy-forward to the attached `PannerNode`. New public `Sound.audioBuffer`
|
|
49
|
+
getter to share one decoded buffer across multiple `Sound` instances.
|
|
50
|
+
- **`LutFilter`** — new colour-pipeline primitive that maps every pixel through a
|
|
51
|
+
Look-Up Table texture. Supports both 1D LUTs (`N×1`, indexed by red channel — palette
|
|
52
|
+
cycling, indexed-colour effects) and 3D LUTs (`N²×N` unwrapped cube with trilinear
|
|
53
|
+
slice interpolation — cinematic colour grading, tone mapping, film stock emulation,
|
|
54
|
+
accessibility filters). Backend selection is automatic. Static helpers
|
|
55
|
+
`LutFilter.identityLut1D(size)`, `LutFilter.identityLut3D(size)`,
|
|
56
|
+
`LutFilter.fromImage(image)` cover the standard DaVinci/OBS/Photoshop LUT-export
|
|
57
|
+
workflows.
|
|
58
|
+
- **`CompressorFilter.reduction`** — public getter forwarding the live gain reduction
|
|
59
|
+
in dB from the underlying `DynamicsCompressorNode`. Use as a meter source for
|
|
60
|
+
visualisations or sidechain triggers.
|
|
61
|
+
|
|
62
|
+
### Examples
|
|
63
|
+
|
|
64
|
+
- Migrated `examples/public/examples/` to a 19-chapter pedagogical structure: getting
|
|
65
|
+
started, application & scenes, sprites & textures, tweens & animation, input, scene
|
|
66
|
+
graph, audio basics, spatial audio, filters, particles, text & fonts, geometry &
|
|
67
|
+
graphics, render targets, performance, audio FX, beat detection, debug layer, custom
|
|
68
|
+
renderers, showcase. Old chapter directories (`collision-detection`, `extras`,
|
|
69
|
+
`particle-system`, `rendering`, `webgpu`) removed.
|
|
70
|
+
- New examples: `spatial-audio/falloff-curves.js`, `filters/palette-cycling.js`,
|
|
71
|
+
`showcase/color-grading.js`. The compressor demo gained a live gain-reduction meter.
|
|
72
|
+
|
|
73
|
+
### Tooling
|
|
74
|
+
|
|
75
|
+
- ESLint config consolidated into a single `eslint.config.ts` driven by ESLint 10 +
|
|
76
|
+
`typescript-eslint`'s type-aware checks plus `simple-import-sort`,
|
|
77
|
+
`unused-imports`, `unicorn`, and `security` plugins. `lint:strict` is the
|
|
78
|
+
release-gate variant, scoped to `src/**/*.ts` and run with `--max-warnings=0` (warnings
|
|
79
|
+
fail the build); `lint` is the broader development view across `src`, `test`, and
|
|
80
|
+
examples. Per-subsystem override blocks are documented as known deviations to tighten
|
|
81
|
+
over time.
|
|
82
|
+
- Tightened to error: `eqeqeq`, `no-floating-promises`, `no-base-to-string`,
|
|
83
|
+
`only-throw-error`, `switch-exhaustiveness-check`, `no-non-null-assertion`,
|
|
84
|
+
`complexity` (cap 20). Added: `no-self-compare`, `no-unreachable-loop`,
|
|
85
|
+
`default-case-last`, `prefer-promise-reject-errors`, `no-promise-executor-return`,
|
|
86
|
+
`no-unmodified-loop-condition`, plus six TypeScript and six Unicorn correctness rules.
|
|
87
|
+
- Prettier `printWidth: 160`, `.editorconfig` matched. Engine code reformatted to
|
|
88
|
+
2-space indent.
|
|
89
|
+
|
|
90
|
+
## [0.8.0] - 2026-05-07
|
|
91
|
+
|
|
92
|
+
Wholesale rewrite of the particle subsystem around a data-oriented core
|
|
93
|
+
plus a backend-agnostic auto-routing pipeline. The `Particle` class,
|
|
94
|
+
`ParticleAffector` interface, `ParticleEmitter` interface,
|
|
95
|
+
`ParticleOptions`, `UniversalEmitter`, and the four built-in affectors
|
|
96
|
+
(`ColorAffector`, `ForceAffector`, `ScaleAffector`, `TorqueAffector`)
|
|
97
|
+
are removed. They are replaced by SoA storage on the system,
|
|
98
|
+
`Distribution<T>`-based spawn configs, and per-batch
|
|
99
|
+
`SpawnModule` / `UpdateModule` / `DeathModule` interfaces.
|
|
100
|
+
|
|
101
|
+
Update modules now declare an optional `wgsl()` contribution — when
|
|
102
|
+
the system is constructed with a `WebGpuBackend` and every registered
|
|
103
|
+
update module is GPU-eligible (i.e. all built-ins, plus any custom
|
|
104
|
+
modules the author opts in), a composite WGSL compute shader is built
|
|
105
|
+
at first `update()`. Integration + every module body + pack-instances
|
|
106
|
+
all run in **one dispatch**, writing directly into the renderer's
|
|
107
|
+
instance vertex buffer. **No CPU readback** in the steady state.
|
|
108
|
+
|
|
109
|
+
On WebGL2 backends, or when any registered update module lacks
|
|
110
|
+
`wgsl()`, the system runs the existing CPU pipeline. The decision is
|
|
111
|
+
automatic and per-system; user code is unchanged across both paths.
|
|
112
|
+
|
|
113
|
+
### Added — Struct-of-Arrays storage
|
|
114
|
+
|
|
115
|
+
`ParticleSystem` now stores particles as parallel `Float32Array` /
|
|
116
|
+
`Uint32Array` / `Uint16Array` channels addressed by slot index:
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
system.posX[slot];
|
|
120
|
+
system.posY[slot];
|
|
121
|
+
system.velX[slot];
|
|
122
|
+
system.velY[slot];
|
|
123
|
+
system.scaleX[slot];
|
|
124
|
+
system.scaleY[slot];
|
|
125
|
+
system.rotations[slot];
|
|
126
|
+
system.rotationSpeeds[slot];
|
|
127
|
+
system.color[slot]; // packed 0xAABBGGRR
|
|
128
|
+
system.elapsed[slot];
|
|
129
|
+
system.lifetime[slot];
|
|
130
|
+
system.textureIndex[slot];
|
|
131
|
+
system.liveCount; // [0, liveCount) is the live range
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Capacity is fixed at construction (default 4096) — no reallocations.
|
|
135
|
+
The integrate pass runs as one tight loop over typed arrays with no
|
|
136
|
+
method calls. Expiry is handled by forward-compaction (O(n) total
|
|
137
|
+
instead of the previous O(n²) splice loop with scattered expirations).
|
|
138
|
+
|
|
139
|
+
### Added — `Distribution<T>` family
|
|
140
|
+
|
|
141
|
+
Spawn-time random sampling and lifetime-parameterised evaluation:
|
|
142
|
+
|
|
143
|
+
| Type | Use |
|
|
144
|
+
| --------------- | ----------------------------------------------------------------------------- |
|
|
145
|
+
| `Constant<T>` | Always-same value |
|
|
146
|
+
| `Range` | Uniform random number in `[min, max]` |
|
|
147
|
+
| `VectorRange` | Per-axis random vector |
|
|
148
|
+
| `ConeDirection` | Random unit vector in a cone × speed range |
|
|
149
|
+
| `CircleArea` | Random point in/on a circle |
|
|
150
|
+
| `BoxArea` | Random point in/on an AABB |
|
|
151
|
+
| `LineSegment` | Random point on a segment |
|
|
152
|
+
| `Curve` | Piecewise-linear keyframe scalar by lifetime ratio |
|
|
153
|
+
| `Gradient` | Piecewise-linear keyframe color, with `evaluateRgba()` for direct u32 packing |
|
|
154
|
+
|
|
155
|
+
`Curve` and `Gradient` cache the last segment so monotonically
|
|
156
|
+
advancing `t` (the typical case for per-particle lifetime sampling)
|
|
157
|
+
is O(1) amortised.
|
|
158
|
+
|
|
159
|
+
### Added — Module pipeline
|
|
160
|
+
|
|
161
|
+
Three module bases. Each registered on a system via the corresponding
|
|
162
|
+
`addX` method; each runs in its declared phase per-frame.
|
|
163
|
+
|
|
164
|
+
```ts
|
|
165
|
+
abstract class SpawnModule {
|
|
166
|
+
apply(system, dt: number): void;
|
|
167
|
+
}
|
|
168
|
+
abstract class UpdateModule {
|
|
169
|
+
apply(system, dt: number): void;
|
|
170
|
+
}
|
|
171
|
+
abstract class DeathModule {
|
|
172
|
+
onDeath(system, slot: number): void;
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
**Built-in spawn modules:**
|
|
177
|
+
|
|
178
|
+
- `RateSpawn({ rate, lifetime?, position?, velocity?, scale?, rotation?, rotationSpeed?, tint?, textureIndex? })`
|
|
179
|
+
— continuous emission with sub-frame accumulator. Each property is an
|
|
180
|
+
independent `Distribution<T>`.
|
|
181
|
+
- `BurstSpawn({ schedule, loop?, ...samePropsAsRate })` — discrete
|
|
182
|
+
bursts at scheduled times. Use for explosions, level-ups,
|
|
183
|
+
hit-impacts.
|
|
184
|
+
|
|
185
|
+
**Built-in update modules** (operate on the SoA arrays in tight loops):
|
|
186
|
+
|
|
187
|
+
- `ApplyForce(ax, ay)` — adds constant acceleration.
|
|
188
|
+
- `Drag(coefficient)` — exponential velocity damping.
|
|
189
|
+
- `ColorOverLifetime(gradient)` — tint sampled from a `Gradient`.
|
|
190
|
+
- `ScaleOverLifetime(curve)` — both axes sampled from a `Curve`.
|
|
191
|
+
- `RotateOverLifetime(angularAccel)` — increments `rotationSpeed`.
|
|
192
|
+
|
|
193
|
+
**Built-in death module:**
|
|
194
|
+
|
|
195
|
+
- `SpawnOnDeath(targetSystem, spawner, count?)` — sub-emitter. Forwards
|
|
196
|
+
the dying particle's position to a target system's spawn module.
|
|
197
|
+
Use for explosion-on-impact, end-of-life sparks, multi-stage VFX.
|
|
198
|
+
|
|
199
|
+
### Added — Backend-agnostic auto-routing GPU compute pipeline
|
|
200
|
+
|
|
201
|
+
New `src/rendering/webgpu/compute/` infrastructure:
|
|
202
|
+
|
|
203
|
+
- `WebGpuStorageBuffer` — owning wrapper over a `STORAGE | COPY_DST | COPY_SRC`
|
|
204
|
+
buffer with `write()` and async `read()` helpers.
|
|
205
|
+
- `WebGpuComputePipeline` — `device.createComputePipeline` wrapper with
|
|
206
|
+
bind-group-layout creation, dispatch helper.
|
|
207
|
+
|
|
208
|
+
New `src/particles/gpu/ParticleGpuState` — owns the GPU-side mirror
|
|
209
|
+
for one `ParticleSystem`. At construction time it:
|
|
210
|
+
|
|
211
|
+
1. Walks the registered update modules, collecting each module's
|
|
212
|
+
`WgslContribution` (uniform field declarations + texture bindings
|
|
213
|
+
- WGSL body snippet).
|
|
214
|
+
2. Generates a composite WGSL compute shader: SoA storage bindings +
|
|
215
|
+
sim/module uniform structs + module texture bindings + a `main`
|
|
216
|
+
function containing integration → all module bodies in registration
|
|
217
|
+
order → pack-instances writing interleaved 24-byte instances into
|
|
218
|
+
a `STORAGE | VERTEX` buffer.
|
|
219
|
+
3. Allocates 7 packed storage buffers (positions/velocities/scales/
|
|
220
|
+
rotInfo/timing as `vec2<f32>` arrays plus color as `u32` plus the
|
|
221
|
+
instance output) — fits within WebGPU's default
|
|
222
|
+
`maxStorageBuffersPerShaderStage = 8` limit.
|
|
223
|
+
4. Allocates 1D textures for any module that declares them
|
|
224
|
+
(`Curve` → 256-tap r32float; `Gradient` → 256-tap rgba8unorm) and
|
|
225
|
+
uploads the lookup data once via `module.uploadTextures()`.
|
|
226
|
+
5. Each module's `writeUniforms()` runs every frame to update its
|
|
227
|
+
slice of the shared module-uniform buffer.
|
|
228
|
+
|
|
229
|
+
The `WebGpuParticleRenderer` reads the GPU-written instance buffer
|
|
230
|
+
directly when `system.gpuMode` is true; CPU mode falls back to the
|
|
231
|
+
existing CPU-pack path. Same renderer, same vertex layout, no copy
|
|
232
|
+
between simulation and render.
|
|
233
|
+
|
|
234
|
+
`UpdateModule` gains optional `wgsl()`, `writeUniforms()`,
|
|
235
|
+
`uploadTextures()`. Built-in modules ship all three. Custom modules
|
|
236
|
+
that implement them get GPU acceleration; modules with only `apply()`
|
|
237
|
+
keep working but force their host system into CPU mode.
|
|
238
|
+
|
|
239
|
+
Opt-in is a single constructor option — no imperative toggle:
|
|
240
|
+
|
|
241
|
+
```ts
|
|
242
|
+
const system = new ParticleSystem(texture, {
|
|
243
|
+
capacity: 8192,
|
|
244
|
+
backend: app.backend, // CPU-routed on WebGL2, GPU-routed on WebGPU
|
|
245
|
+
});
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
The `backend` reference is duck-typed against `WebGpuBackend`; on
|
|
249
|
+
WebGL2 it's recorded but never used. The system's mode is locked in
|
|
250
|
+
at the first `update()` (when modules are introspected); adding update
|
|
251
|
+
modules after that throws.
|
|
252
|
+
|
|
253
|
+
### Removed — Old particle API (BREAKING)
|
|
254
|
+
|
|
255
|
+
The following symbols are deleted. Migration recipes follow the table.
|
|
256
|
+
|
|
257
|
+
| Removed | Replacement |
|
|
258
|
+
| -------------------------------------- | ------------------------------------------------------------- |
|
|
259
|
+
| `Particle` (class) | SoA arrays on `ParticleSystem` (`system.posX[slot]`, etc.) |
|
|
260
|
+
| `ParticleProperties` (interface) | None — slot-indexed arrays replace the per-particle object |
|
|
261
|
+
| `ParticleEmitter` (interface) | `SpawnModule` (abstract class) |
|
|
262
|
+
| `ParticleOptions` | Per-property `Distribution<T>` in the spawn module's config |
|
|
263
|
+
| `UniversalEmitter` | `RateSpawn` |
|
|
264
|
+
| `ParticleAffector` (interface) | `UpdateModule` (abstract class) |
|
|
265
|
+
| `ColorAffector` | `ColorOverLifetime` + `Gradient` |
|
|
266
|
+
| `ForceAffector` | `ApplyForce` |
|
|
267
|
+
| `ScaleAffector` | `ScaleOverLifetime` + `Curve` |
|
|
268
|
+
| `TorqueAffector` | `RotateOverLifetime` |
|
|
269
|
+
| `system.requestParticle()` | `system.spawn(): number` (slot index, or `-1` at capacity) |
|
|
270
|
+
| `system.emitParticle(p)` | (gone — `spawn()` already commits the slot to the live range) |
|
|
271
|
+
| `system.updateParticle(p, dt)` | (gone — internal to `update()`) |
|
|
272
|
+
| `system.addEmitter(e)` | `system.addSpawnModule(m)` |
|
|
273
|
+
| `system.addAffector(a)` | `system.addUpdateModule(m)` |
|
|
274
|
+
| `system.particles` (`Array<Particle>`) | `system.posX` / `system.posY` / ... `system.liveCount` |
|
|
275
|
+
| `system.graveyard` | (gone — no graveyard; slots are recycled in place) |
|
|
276
|
+
|
|
277
|
+
### Migration
|
|
278
|
+
|
|
279
|
+
```ts
|
|
280
|
+
// Before — bonfire
|
|
281
|
+
const options = new ParticleOptions();
|
|
282
|
+
const colorAffector = new ColorAffector(new Color(194, 64, 30, 1), new Color(0, 0, 0, 0));
|
|
283
|
+
const emitter = new UniversalEmitter(50, options);
|
|
284
|
+
const system = new ParticleSystem(texture);
|
|
285
|
+
system.addAffector(colorAffector);
|
|
286
|
+
system.addEmitter(emitter);
|
|
287
|
+
|
|
288
|
+
// in update():
|
|
289
|
+
options.totalLifetime.copy(seconds(rand(5, 10)));
|
|
290
|
+
options.position.set(rand(-50, 50), rand(-10, 10));
|
|
291
|
+
options.velocity.set(/* ... */);
|
|
292
|
+
|
|
293
|
+
// After — bonfire
|
|
294
|
+
const system = new ParticleSystem(texture);
|
|
295
|
+
system.addSpawnModule(
|
|
296
|
+
new RateSpawn({
|
|
297
|
+
rate: new Constant(50),
|
|
298
|
+
lifetime: new Range(5, 10),
|
|
299
|
+
position: new VectorRange(-50, 50, -10, 10),
|
|
300
|
+
velocity: new ConeDirection(-Math.PI / 2, Math.PI / 36, 60, 80),
|
|
301
|
+
}),
|
|
302
|
+
);
|
|
303
|
+
system.addUpdateModule(
|
|
304
|
+
new ColorOverLifetime(
|
|
305
|
+
new Gradient([
|
|
306
|
+
{ t: 0, color: new Color(194, 64, 30, 1) },
|
|
307
|
+
{ t: 1, color: new Color(0, 0, 0, 0) },
|
|
308
|
+
]),
|
|
309
|
+
),
|
|
310
|
+
);
|
|
311
|
+
// no per-frame mutation needed.
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
```ts
|
|
315
|
+
// Before — gravity affector
|
|
316
|
+
const gravity = new ForceAffector(0, 980);
|
|
317
|
+
system.addAffector(gravity);
|
|
318
|
+
|
|
319
|
+
// After
|
|
320
|
+
system.addUpdateModule(new ApplyForce(0, 980));
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
```ts
|
|
324
|
+
// Before — custom affector
|
|
325
|
+
class AlphaFade {
|
|
326
|
+
apply(particle, delta) {
|
|
327
|
+
particle.tint.a = particle.remainingRatio;
|
|
328
|
+
return this;
|
|
329
|
+
}
|
|
330
|
+
destroy() {}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// After
|
|
334
|
+
class AlphaFadeOverLifetime extends UpdateModule {
|
|
335
|
+
apply(system) {
|
|
336
|
+
const { color, elapsed, lifetime, liveCount } = system;
|
|
337
|
+
for (let i = 0; i < liveCount; i++) {
|
|
338
|
+
const remaining = 1 - elapsed[i] / lifetime[i];
|
|
339
|
+
const a = (Math.max(0, Math.min(1, remaining)) * 255) & 255;
|
|
340
|
+
color[i] = (color[i] & 0x00ffffff) | (a << 24);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
```ts
|
|
347
|
+
// Before — direct particle creation in tests
|
|
348
|
+
const particle = system.requestParticle();
|
|
349
|
+
particle.position.set(10, 12);
|
|
350
|
+
particle.tint = Color.red;
|
|
351
|
+
system.emitParticle(particle);
|
|
352
|
+
|
|
353
|
+
// After — direct slot manipulation
|
|
354
|
+
const slot = system.spawn();
|
|
355
|
+
system.posX[slot] = 10;
|
|
356
|
+
system.posY[slot] = 12;
|
|
357
|
+
system.color[slot] = Color.red.toRgba();
|
|
358
|
+
system.lifetime[slot] = 1;
|
|
359
|
+
system.scaleX[slot] = 1;
|
|
360
|
+
system.scaleY[slot] = 1;
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
### Changed — `ParticleSystem` constructor: typed overloads (BREAKING)
|
|
364
|
+
|
|
365
|
+
Source material (texture / atlas frames / spritesheet) lives in
|
|
366
|
+
**positional arguments** — TypeScript overload signatures enforce mutual
|
|
367
|
+
exclusivity at compile time so you can't pass nonsense combinations like
|
|
368
|
+
texture-and-spritesheet-at-once. Capacity and the test-only `device`
|
|
369
|
+
escape hatch live in the trailing options object.
|
|
370
|
+
|
|
371
|
+
```ts
|
|
372
|
+
// 0.7.x:
|
|
373
|
+
new ParticleSystem(texture);
|
|
374
|
+
new ParticleSystem(texture, 4096);
|
|
375
|
+
|
|
376
|
+
// 0.8.0:
|
|
377
|
+
new ParticleSystem(); // untextured (1×1 white), CPU/GPU auto-routed
|
|
378
|
+
new ParticleSystem(spark); // simple textured particles
|
|
379
|
+
new ParticleSystem(spark, { capacity: 8192 }); // explicit capacity
|
|
380
|
+
new ParticleSystem(atlas, [r0, r1, r2]); // multi-frame atlas
|
|
381
|
+
new ParticleSystem(atlas, frames, { capacity: 8192 }); // atlas + capacity
|
|
382
|
+
new ParticleSystem(sheet); // spritesheet shorthand
|
|
383
|
+
new ParticleSystem(sheet, { capacity: 4096 });
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
The four overload signatures:
|
|
387
|
+
|
|
388
|
+
```ts
|
|
389
|
+
constructor(options?: ParticleSystemOptions);
|
|
390
|
+
constructor(texture: Texture, options?: ParticleSystemOptions);
|
|
391
|
+
constructor(texture: Texture, frames: ReadonlyArray<Rectangle>, options?: ParticleSystemOptions);
|
|
392
|
+
constructor(spritesheet: Spritesheet, options?: ParticleSystemOptions);
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
Compile-time errors for illegal combinations:
|
|
396
|
+
|
|
397
|
+
```ts
|
|
398
|
+
new ParticleSystem(spark, sheet); // ✗ no overload matches
|
|
399
|
+
new ParticleSystem(sheet, frames); // ✗ frames only valid with Texture
|
|
400
|
+
new ParticleSystem({ frames }); // ✗ frames isn't an option
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
**No `backend` option** — the renderer auto-discovers the active backend
|
|
404
|
+
on the first `render(backend)` call. WebGPU → GPU compute path, WebGL2 →
|
|
405
|
+
CPU path. Re-discovery on backend change (device-loss recovery).
|
|
406
|
+
|
|
407
|
+
### Added — Optional texture + 1×1 white default
|
|
408
|
+
|
|
409
|
+
When `texture` is omitted, the system uses a lazily-allocated 1×1
|
|
410
|
+
opaque-white singleton. Particles render as solid color quads driven by
|
|
411
|
+
the per-particle `color` channel. Useful for tech-demo magic effects,
|
|
412
|
+
abstract VFX, performance benchmarks.
|
|
413
|
+
|
|
414
|
+
### Added — Multi-frame atlas via `frames` / `spritesheet` options
|
|
415
|
+
|
|
416
|
+
`frames: ReadonlyArray<Rectangle>` declares per-particle frame
|
|
417
|
+
rectangles within the atlas texture. Each particle's `textureIndex[i]`
|
|
418
|
+
selects which frame to render — `RateSpawn` /
|
|
419
|
+
`BurstSpawn`'s `textureIndex: Distribution<number>` becomes the per-spawn
|
|
420
|
+
frame chooser:
|
|
421
|
+
|
|
422
|
+
```ts
|
|
423
|
+
const system = new ParticleSystem({
|
|
424
|
+
texture: explosionAtlas,
|
|
425
|
+
frames: [
|
|
426
|
+
new Rectangle(0, 0, 32, 32), // index 0 — flame core
|
|
427
|
+
new Rectangle(32, 0, 32, 32), // index 1 — smoke ring
|
|
428
|
+
new Rectangle(64, 0, 32, 32), // index 2 — ember
|
|
429
|
+
],
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
system.addSpawnModule(
|
|
433
|
+
new BurstSpawn({
|
|
434
|
+
schedule: [{ time: 0, count: 60 }],
|
|
435
|
+
velocity: ConeDirection.omni(120, 280),
|
|
436
|
+
textureIndex: new Range(0, 2), // each spawn picks a random frame
|
|
437
|
+
}),
|
|
438
|
+
);
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
`Spritesheet` integration via `spritesheet: sheet` extracts texture +
|
|
442
|
+
frames in insertion order — convenient for atlas authors who already
|
|
443
|
+
have a sheet from a TexturePacker / Aseprite export.
|
|
444
|
+
|
|
445
|
+
UV resolution happens once per particle per frame (CPU pack in CPU mode,
|
|
446
|
+
compute shader in GPU mode); the renderer reads pre-resolved UVs from
|
|
447
|
+
the instance buffer — no shader-side frame-array lookup overhead.
|
|
448
|
+
|
|
449
|
+
### Changed — Per-instance vertex layout: 24 → 40 bytes
|
|
450
|
+
|
|
451
|
+
The renderer's per-instance buffer now carries `uvMin: vec2` and
|
|
452
|
+
`uvMax: vec2` alongside the existing translation/scale/rotation/color
|
|
453
|
+
fields. Lets a single batch render any mix of atlas frames per instance
|
|
454
|
+
without indirection through a uniform array. Net cost: +67% bandwidth
|
|
455
|
+
on the instance buffer (still trivial — ~10 MB/s at 60 fps with 16k
|
|
456
|
+
particles).
|
|
457
|
+
|
|
458
|
+
The previous design used a single `u_uvBounds` uniform that pinned
|
|
459
|
+
every particle in a system to the same frame; the new layout is what
|
|
460
|
+
makes per-particle atlas selection free.
|
|
461
|
+
|
|
462
|
+
The system pre-allocates all SoA arrays at construction. Spawn modules
|
|
463
|
+
that want to emit beyond capacity get `-1` from `spawn()` and should
|
|
464
|
+
bail cleanly (the built-ins do).
|
|
465
|
+
|
|
466
|
+
### Changed — slot allocation differs between CPU and GPU mode
|
|
467
|
+
|
|
468
|
+
In CPU mode, `[0, liveCount)` is dense (forward-compaction at end of
|
|
469
|
+
update). `spawn()` always returns the next sequential slot.
|
|
470
|
+
|
|
471
|
+
In GPU mode, no compaction happens — readback would be required to
|
|
472
|
+
move slots whose authoritative position lives in GPU memory. Instead:
|
|
473
|
+
|
|
474
|
+
- Each particle has an `alive: Uint8Array` flag (1 = alive, 0 = dead).
|
|
475
|
+
- `spawn()` finds the first dead slot via a round-robin hint pointer
|
|
476
|
+
(amortised O(1), worst case O(capacity)).
|
|
477
|
+
- Expiry on CPU: `alive[i] = 0`, `lifetime[i] = -1` (sentinel).
|
|
478
|
+
- The compute shader skips dead slots (`timing[idx].y < 0.0` → write
|
|
479
|
+
zero-scale instance and return).
|
|
480
|
+
|
|
481
|
+
Custom modules iterating `[0, liveCount)` should check `system.alive[i]`
|
|
482
|
+
in GPU mode if they care about ignoring dead slots; mutating dead slot
|
|
483
|
+
data is harmless because the GPU shader skips them.
|
|
484
|
+
|
|
485
|
+
### Added — `system.aliveCount`
|
|
486
|
+
|
|
487
|
+
Returns the actual count of alive particles (slots with `alive[i] === 1`).
|
|
488
|
+
In CPU mode this equals `liveCount`; in GPU mode it's `≤ liveCount`.
|
|
489
|
+
Use for fragmentation diagnostics or UI counters.
|
|
490
|
+
|
|
491
|
+
### Performance notes
|
|
492
|
+
|
|
493
|
+
- Spawning + integrating + ColorOverLifetime/ScaleOverLifetime + drag
|
|
494
|
+
on 10k particles: previously ~5 ms CPU per frame; new SoA path on
|
|
495
|
+
CPU: ~0.5 ms (~10× speedup from eliminating per-particle object
|
|
496
|
+
indirection). New GPU path on WebGPU: ~0.05 ms (~100× speedup from
|
|
497
|
+
the previous OO baseline) — bound by the per-frame upload, not the
|
|
498
|
+
compute itself.
|
|
499
|
+
- The crossover where GPU beats CPU sits around 1-3 k particles
|
|
500
|
+
depending on hardware. For sub-1k systems CPU is still slightly
|
|
501
|
+
faster (upload overhead dominates); the auto-router doesn't second-
|
|
502
|
+
guess this — opt out via `backend: undefined` if you want to force
|
|
503
|
+
CPU at low counts.
|
|
504
|
+
- 100k+ particles render and simulate cleanly on WebGPU at 60 fps in
|
|
505
|
+
CI smoke tests; the bottleneck shifts from compute to texture
|
|
506
|
+
bandwidth at that scale.
|
|
507
|
+
|
|
7
508
|
## [0.7.13] - 2026-05-07
|
|
8
509
|
|
|
9
510
|
Major gamepad-input refactor. Replaces the `new Input(...)` +
|
|
@@ -19,7 +520,7 @@ signed stick channels, and Joy-Con-honest mappings.
|
|
|
19
520
|
```ts
|
|
20
521
|
// Per inputManager (manual unbind):
|
|
21
522
|
app.input.onTrigger(GamepadButton.South, () => player.jump());
|
|
22
|
-
app.input.onActive(GamepadAxis.LeftStickX,
|
|
523
|
+
app.input.onActive(GamepadAxis.LeftStickX, v => (player.x += v * 5));
|
|
23
524
|
app.input.onStart([Keyboard.Space, GamepadButton.South], () => fire());
|
|
24
525
|
|
|
25
526
|
// Per gamepad (slot-aware, listener survives disconnect/reconnect):
|
|
@@ -63,8 +564,8 @@ default `'sticky'` (each pad keeps its slot through disconnects).
|
|
|
63
564
|
disconnect (good for hot-seat couch coop where "the first N pads are
|
|
64
565
|
the N players" is the desired semantic).
|
|
65
566
|
|
|
66
|
-
In compact mode, the disconnect signal fires on the slot that
|
|
67
|
-
|
|
567
|
+
In compact mode, the disconnect signal fires on the slot that _ended
|
|
568
|
+
up_ empty after the shift (not the slot the disconnected hardware
|
|
68
569
|
originally occupied), keeping `pad.connected === false` consistent with
|
|
69
570
|
the fired event. Slots that received a different physical pad through
|
|
70
571
|
the shift dispatch a separate signal:
|
|
@@ -78,6 +579,7 @@ player when slots renumber.
|
|
|
78
579
|
### Added — Generic signals
|
|
79
580
|
|
|
80
581
|
Per-pad:
|
|
582
|
+
|
|
81
583
|
- `pad.onConnect: Signal<[]>`
|
|
82
584
|
- `pad.onDisconnect: Signal<[]>`
|
|
83
585
|
- `pad.onButtonDown: Signal<[GamepadButton, number]>`
|
|
@@ -85,6 +587,7 @@ Per-pad:
|
|
|
85
587
|
- `pad.onAxisChange: Signal<[GamepadAxis, number]>`
|
|
86
588
|
|
|
87
589
|
Aggregate across all pads:
|
|
590
|
+
|
|
88
591
|
- `inputManager.onAnyGamepadButtonDown: Signal<[Gamepad, GamepadButton, number]>`
|
|
89
592
|
- `inputManager.onAnyGamepadButtonUp: Signal<[Gamepad, GamepadButton, number]>`
|
|
90
593
|
- `inputManager.onAnyGamepadAxisChange: Signal<[Gamepad, GamepadAxis, number]>`
|
|
@@ -93,7 +596,7 @@ Aggregate across all pads:
|
|
|
93
596
|
|
|
94
597
|
```ts
|
|
95
598
|
if (pad.canVibrate) {
|
|
96
|
-
|
|
599
|
+
await pad.vibrate({ duration: 200, weakMagnitude: 0.5, strongMagnitude: 1.0 });
|
|
97
600
|
}
|
|
98
601
|
pad.stopVibration();
|
|
99
602
|
```
|
|
@@ -114,18 +617,18 @@ remain available for buttons-style 0..1 input.
|
|
|
114
617
|
|
|
115
618
|
```ts
|
|
116
619
|
// Stick-style — one binding per axis, signed value:
|
|
117
|
-
this.inputs.onActive(GamepadAxis.LeftStickX,
|
|
620
|
+
this.inputs.onActive(GamepadAxis.LeftStickX, x => (player.x += x * 5));
|
|
118
621
|
|
|
119
622
|
// Buttons-style — separate bindings per direction, 0..1 each:
|
|
120
|
-
this.inputs.onActive(GamepadAxis.LeftStickLeft,
|
|
121
|
-
this.inputs.onActive(GamepadAxis.LeftStickRight,
|
|
623
|
+
this.inputs.onActive(GamepadAxis.LeftStickLeft, v => (player.x -= v * 5));
|
|
624
|
+
this.inputs.onActive(GamepadAxis.LeftStickRight, v => (player.x += v * 5));
|
|
122
625
|
```
|
|
123
626
|
|
|
124
627
|
### Added — `pad.hasChannel(channel)` capability check
|
|
125
628
|
|
|
126
629
|
```ts
|
|
127
630
|
if (pad.hasChannel(GamepadAxis.RightStickX)) {
|
|
128
|
-
|
|
631
|
+
pad.onActive(GamepadAxis.RightStickX, v => (crosshair.x += v * 8));
|
|
129
632
|
}
|
|
130
633
|
```
|
|
131
634
|
|
|
@@ -142,19 +645,19 @@ Internally tracks each binding and calls `.unbind()` in `Scene.destroy`.
|
|
|
142
645
|
### Added — Steam Deck / Steam Virtual Gamepad / Valve fallback
|
|
143
646
|
|
|
144
647
|
New `SteamDeckGamepadMapping` covers the raw HID layout reported by the
|
|
145
|
-
Steam Deck (and likely future Valve hardware) when Steam Input is
|
|
648
|
+
Steam Deck (and likely future Valve hardware) when Steam Input is _not_
|
|
146
649
|
intercepting the device. Indices follow the SDL_GameControllerDB Linux
|
|
147
650
|
entry: face buttons at 3-6, D-pad at 16-19, paddles at 20-23, triggers
|
|
148
651
|
as analog axes 8/9.
|
|
149
652
|
|
|
150
653
|
Routing rules added to `builtInGamepadDefinitions`:
|
|
151
654
|
|
|
152
|
-
| Browser ID
|
|
153
|
-
|
|
154
|
-
| `28de:1102`, `28de:1142`
|
|
155
|
-
| `28de:11ff` (Steam Virtual Gamepad — any controller via Steam Input)
|
|
156
|
-
| `28de:1205`
|
|
157
|
-
| Vendor `28de` (anything else from Valve, e.g. future Steam Controller 2 raw) | `SteamDeckGamepadMapping` (best-effort fallback)
|
|
655
|
+
| Browser ID | Mapping |
|
|
656
|
+
| ---------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
|
|
657
|
+
| `28de:1102`, `28de:1142` | `SteamControllerGamepadMapping` (existing, original Steam Controller raw) |
|
|
658
|
+
| `28de:11ff` (Steam Virtual Gamepad — any controller via Steam Input) | `GenericDualAnalogGamepadMapping` (W3C standard Xbox emulation) |
|
|
659
|
+
| `28de:1205` | `SteamDeckGamepadMapping` (raw Steam Deck) |
|
|
660
|
+
| Vendor `28de` (anything else from Valve, e.g. future Steam Controller 2 raw) | `SteamDeckGamepadMapping` (best-effort fallback) |
|
|
158
661
|
|
|
159
662
|
Enum: `GamepadMappingFamily.SteamDeck` added.
|
|
160
663
|
|
|
@@ -207,7 +710,7 @@ app.input.getGamepad(0);
|
|
|
207
710
|
### Fixed — Compact-mode disconnect ordering
|
|
208
711
|
|
|
209
712
|
In `'compact'` slot strategy, `onDisconnect` previously fired on the
|
|
210
|
-
slot the disconnected hardware originally occupied —
|
|
713
|
+
slot the disconnected hardware originally occupied — _before_ the
|
|
211
714
|
compaction shift moved a different physical pad into that slot. User
|
|
212
715
|
code observing the event would see `pad.connected === true` because
|
|
213
716
|
the slot had been silently re-bound by the shift. Now compaction is
|
|
@@ -219,13 +722,13 @@ ended up empty (the trailing slot). Sticky behaviour is unchanged.
|
|
|
219
722
|
The unified `GamepadChannel` enum is split into two disjoint enums for
|
|
220
723
|
nominal type safety:
|
|
221
724
|
|
|
222
|
-
| Old
|
|
223
|
-
|
|
224
|
-
| `GamepadChannel.ButtonSouth`
|
|
225
|
-
| `GamepadChannel.ButtonEast`
|
|
226
|
-
| `GamepadChannel.LeftShoulder`
|
|
227
|
-
| `GamepadChannel.LeftStickLeft` | `GamepadAxis.LeftStickLeft`
|
|
228
|
-
| ...
|
|
725
|
+
| Old | New (user-facing) | New (internal type) |
|
|
726
|
+
| ------------------------------ | ---------------------------- | ----------------------------------- |
|
|
727
|
+
| `GamepadChannel.ButtonSouth` | `GamepadButton.South` | `GamepadButtonChannel.South` |
|
|
728
|
+
| `GamepadChannel.ButtonEast` | `GamepadButton.East` | `GamepadButtonChannel.East` |
|
|
729
|
+
| `GamepadChannel.LeftShoulder` | `GamepadButton.LeftShoulder` | `GamepadButtonChannel.LeftShoulder` |
|
|
730
|
+
| `GamepadChannel.LeftStickLeft` | `GamepadAxis.LeftStickLeft` | `GamepadAxisChannel.LeftStickLeft` |
|
|
731
|
+
| ... | ... | ... |
|
|
229
732
|
|
|
230
733
|
User code references the namespace mirrors (`GamepadButton.X`,
|
|
231
734
|
`GamepadAxis.Y`) — same `Pointer.X` / `Keyboard.Space` convention. Type
|
|
@@ -267,11 +770,11 @@ instead of firing every frame.
|
|
|
267
770
|
|
|
268
771
|
```ts
|
|
269
772
|
// Before:
|
|
270
|
-
new Gamepad(index, channels, mapping)
|
|
271
|
-
new Gamepad(browserGamepad, channels, definition)
|
|
773
|
+
new Gamepad(index, channels, mapping);
|
|
774
|
+
new Gamepad(browserGamepad, channels, definition);
|
|
272
775
|
|
|
273
776
|
// After (engine-internal — InputManager handles slot allocation):
|
|
274
|
-
new Gamepad(slot, channels)
|
|
777
|
+
new Gamepad(slot, channels);
|
|
275
778
|
// followed by pad._bind(browserGamepad, definition) on connect
|
|
276
779
|
```
|
|
277
780
|
|
|
@@ -293,7 +796,7 @@ import { GamepadButton, Keyboard } from '@codexo/exojs';
|
|
|
293
796
|
|
|
294
797
|
// Manual lifecycle
|
|
295
798
|
const binding = app.input.onTrigger(GamepadButton.South, () => player.jump());
|
|
296
|
-
binding.unbind();
|
|
799
|
+
binding.unbind(); // when done
|
|
297
800
|
|
|
298
801
|
// Auto-disposed on scene unload
|
|
299
802
|
this.inputs.onTrigger(GamepadButton.South, () => player.jump());
|
|
@@ -304,37 +807,31 @@ this.app.input.gamepads[0].onTrigger(GamepadButton.South, () => player.jump());
|
|
|
304
807
|
|
|
305
808
|
```ts
|
|
306
809
|
// Stick movement — before:
|
|
307
|
-
const moveLeft
|
|
810
|
+
const moveLeft = new Input(GamepadChannel.LeftStickLeft);
|
|
308
811
|
const moveRight = new Input(GamepadChannel.LeftStickRight);
|
|
309
812
|
app.input.add(moveLeft);
|
|
310
813
|
app.input.add(moveRight);
|
|
311
814
|
// per frame: const x = moveRight.value - moveLeft.value;
|
|
312
815
|
|
|
313
816
|
// After (signed aggregate channel):
|
|
314
|
-
this.inputs.onActive(GamepadAxis.LeftStickX,
|
|
817
|
+
this.inputs.onActive(GamepadAxis.LeftStickX, x => (player.x += x * 5));
|
|
315
818
|
```
|
|
316
819
|
|
|
317
820
|
```ts
|
|
318
821
|
// Custom mapping — before:
|
|
319
822
|
import { GamepadMapping, GamepadChannel } from '@codexo/exojs';
|
|
320
823
|
const buttons = GamepadMapping.createControls([
|
|
321
|
-
|
|
322
|
-
|
|
824
|
+
[0, GamepadChannel.ButtonSouth],
|
|
825
|
+
[1, GamepadChannel.ButtonEast],
|
|
323
826
|
]);
|
|
324
827
|
|
|
325
828
|
// After:
|
|
326
829
|
import { GamepadButton, GamepadMapping, GamepadMappingFamily } from '@codexo/exojs';
|
|
327
830
|
class MyMapping extends GamepadMapping {
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
new GamepadButton(0, GamepadButton.South),
|
|
333
|
-
new GamepadButton(1, GamepadButton.East),
|
|
334
|
-
],
|
|
335
|
-
[],
|
|
336
|
-
);
|
|
337
|
-
}
|
|
831
|
+
public readonly family = GamepadMappingFamily.GenericDualAnalog;
|
|
832
|
+
public constructor() {
|
|
833
|
+
super([new GamepadButton(0, GamepadButton.South), new GamepadButton(1, GamepadButton.East)], []);
|
|
834
|
+
}
|
|
338
835
|
}
|
|
339
836
|
```
|
|
340
837
|
|
|
@@ -460,7 +957,7 @@ indexing is now automatic and persistent).
|
|
|
460
957
|
audio, collision, scene-graph, interaction. Each domain has its own
|
|
461
958
|
script (`npm run perf:bench:rendering`, `:audio`, `:collision`,
|
|
462
959
|
`:scene-graph`, `:interaction`) plus `:all` aggregator. Output: JSON
|
|
463
|
-
|
|
960
|
+
- Markdown to `test/perf/results/`.
|
|
464
961
|
- **Baseline snapshot** committed as `test/perf/results/baseline.md` —
|
|
465
962
|
reference numbers at 0.7.10 for future regression detection.
|
|
466
963
|
- **Auto-profiler** (`npm run perf:profile`, `:gc` variant with
|
|
@@ -500,6 +997,7 @@ also makes the `useSpatialIndex` opt-in flag unnecessary and **the
|
|
|
500
997
|
flag has been removed entirely**.
|
|
501
998
|
|
|
502
999
|
**How it works now:**
|
|
1000
|
+
|
|
503
1001
|
- A persistent quadtree is created lazily when the first interactive
|
|
504
1002
|
node enters the scene.
|
|
505
1003
|
- `Container.addChild` / `removeChild` walk subtrees and add/remove
|
|
@@ -540,7 +1038,7 @@ actually-moved nodes.
|
|
|
540
1038
|
|
|
541
1039
|
```ts
|
|
542
1040
|
// Before:
|
|
543
|
-
app.interaction.useSpatialIndex = true;
|
|
1041
|
+
app.interaction.useSpatialIndex = true; // flag opt-in
|
|
544
1042
|
|
|
545
1043
|
// After:
|
|
546
1044
|
// Nothing — index is automatic. Just have at least one interactive
|
|
@@ -643,7 +1141,7 @@ Fixes a GLSL compile-error in the 0.7.8 shader auto-upgrade path.
|
|
|
643
1141
|
### Fixed
|
|
644
1142
|
|
|
645
1143
|
- **`upgradeFragmentShaderToGl300()` now always prepends `precision highp
|
|
646
|
-
|
|
1144
|
+
float;`** before the `out vec4 fragColor;` declaration. Previously, if
|
|
647
1145
|
the user's source already contained a precision declaration anywhere
|
|
648
1146
|
(e.g., `precision lowp float;` mid-source), the upgrader skipped its
|
|
649
1147
|
own injection — but the user's declaration came AFTER the
|
|
@@ -654,7 +1152,7 @@ Fixes a GLSL compile-error in the 0.7.8 shader auto-upgrade path.
|
|
|
654
1152
|
|
|
655
1153
|
Multiple precision declarations are legal in GLSL ES 3.00 with
|
|
656
1154
|
last-precision-wins semantics. The fix always injects `precision highp
|
|
657
|
-
|
|
1155
|
+
float;` at line 2 (before `out vec4 fragColor;`); the user's own
|
|
658
1156
|
precision declaration further down still applies to their code via
|
|
659
1157
|
the standard last-precision-wins rule. No semantic change for
|
|
660
1158
|
user-provided shader logic; previously-broken shaders with custom
|
|
@@ -853,7 +1351,7 @@ app.backend.setCursor('pointer');
|
|
|
853
1351
|
const cursor = app.backend.cursor;
|
|
854
1352
|
|
|
855
1353
|
// After:
|
|
856
|
-
app.setCursor('pointer');
|
|
1354
|
+
app.setCursor('pointer'); // or
|
|
857
1355
|
app.cursor = 'pointer';
|
|
858
1356
|
const cursor = app.cursor;
|
|
859
1357
|
```
|
|
@@ -861,14 +1359,14 @@ const cursor = app.cursor;
|
|
|
861
1359
|
```ts
|
|
862
1360
|
// New: react to backend loss
|
|
863
1361
|
app.onBackendLost.add(() => {
|
|
864
|
-
|
|
1362
|
+
showReloadDialog();
|
|
865
1363
|
});
|
|
866
1364
|
|
|
867
1365
|
// Or backend-specific:
|
|
868
1366
|
if (app.backend.backendType === RenderBackendType.WebGpu) {
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
1367
|
+
(app.backend as WebGpuBackend).onDeviceLost.add(info => {
|
|
1368
|
+
console.error('GPU device lost:', info.message, info.reason);
|
|
1369
|
+
});
|
|
872
1370
|
}
|
|
873
1371
|
```
|
|
874
1372
|
|
|
@@ -977,9 +1475,9 @@ etc.).
|
|
|
977
1475
|
separate `WebGpuShaderFilter`.
|
|
978
1476
|
- **Backend guard messages updated**:
|
|
979
1477
|
- `WebGl2ShaderFilter` on WebGPU: `'WebGl2ShaderFilter requires the
|
|
980
|
-
|
|
1478
|
+
WebGL2 backend. Use WebGpuShaderFilter on WebGPU.'`
|
|
981
1479
|
- `WebGpuShaderFilter` on WebGL2: `'WebGpuShaderFilter requires the
|
|
982
|
-
|
|
1480
|
+
WebGPU backend. Use WebGl2ShaderFilter on WebGL2.'`
|
|
983
1481
|
|
|
984
1482
|
`ShaderFilterUniformValue` (the polymorphic uniform value type) is
|
|
985
1483
|
**unchanged** and shared between both backends — same value shapes
|
|
@@ -1040,7 +1538,7 @@ LUT color grading, chromatic aberration, etc.
|
|
|
1040
1538
|
via property assignment; flushed before each apply():
|
|
1041
1539
|
```ts
|
|
1042
1540
|
filter.uniforms.uTime = performance.now() / 1000;
|
|
1043
|
-
filter.uniforms.uColor = [1, 0.5, 0, 1];
|
|
1541
|
+
filter.uniforms.uColor = [1, 0.5, 0, 1]; // vec4
|
|
1044
1542
|
```
|
|
1045
1543
|
- **Polymorphic uniform values**: scalar `number`, tuple `[a, b]` /
|
|
1046
1544
|
`[a, b, c]` / `[a, b, c, d]`, `Float32Array` / `Int32Array`, or
|
|
@@ -1055,8 +1553,8 @@ LUT color grading, chromatic aberration, etc.
|
|
|
1055
1553
|
|
|
1056
1554
|
- **WebGL2-only in V1.** Constructor accepts `wgsl` source, but `apply()`
|
|
1057
1555
|
on the WebGPU backend throws `'ShaderFilter does not yet support the
|
|
1058
|
-
|
|
1059
|
-
|
|
1556
|
+
WebGPU backend. WGSL support is planned for a future release. Use the
|
|
1557
|
+
WebGL2 backend for now.'` Document this limitation; reasoning: WebGPU
|
|
1060
1558
|
requires a separate WGSL pipeline implementation that's substantial
|
|
1061
1559
|
on its own. Coming when there's concrete user demand.
|
|
1062
1560
|
- `fragmentSource` is required at construction. Constructor throws if
|
|
@@ -1073,7 +1571,7 @@ LUT color grading, chromatic aberration, etc.
|
|
|
1073
1571
|
import { ShaderFilter } from '@codexo/exojs';
|
|
1074
1572
|
|
|
1075
1573
|
const filter = new ShaderFilter({
|
|
1076
|
-
|
|
1574
|
+
fragmentSource: `#version 300 es
|
|
1077
1575
|
precision highp float;
|
|
1078
1576
|
in vec2 vUv;
|
|
1079
1577
|
uniform sampler2D uTexture;
|
|
@@ -1086,15 +1584,15 @@ const filter = new ShaderFilter({
|
|
|
1086
1584
|
outColor = texture(uTexture, uv);
|
|
1087
1585
|
}
|
|
1088
1586
|
`,
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1587
|
+
uniforms: {
|
|
1588
|
+
uTime: 0,
|
|
1589
|
+
},
|
|
1092
1590
|
});
|
|
1093
1591
|
|
|
1094
1592
|
sprite.filters = [filter];
|
|
1095
1593
|
|
|
1096
|
-
app.onFrame.add(
|
|
1097
|
-
|
|
1594
|
+
app.onFrame.add(delta => {
|
|
1595
|
+
filter.uniforms.uTime = performance.now() / 1000;
|
|
1098
1596
|
});
|
|
1099
1597
|
```
|
|
1100
1598
|
|
|
@@ -1180,10 +1678,10 @@ const spectrum = analyser.getSpectrum();
|
|
|
1180
1678
|
const waveform = analyser.getWaveform();
|
|
1181
1679
|
|
|
1182
1680
|
// Now also possible:
|
|
1183
|
-
analyser.source = mediaStream;
|
|
1681
|
+
analyser.source = mediaStream; // Mic input
|
|
1184
1682
|
analyser.source = app.audio.master; // Whole mix
|
|
1185
|
-
analyser.getBandEnergy(20, 200);
|
|
1186
|
-
analyser.getLowMidHigh();
|
|
1683
|
+
analyser.getBandEnergy(20, 200); // Bass energy 0..1
|
|
1684
|
+
analyser.getLowMidHigh(); // {low, mid, high}
|
|
1187
1685
|
```
|
|
1188
1686
|
|
|
1189
1687
|
```ts
|
|
@@ -1193,12 +1691,12 @@ detector.source = music;
|
|
|
1193
1691
|
await detector.ready;
|
|
1194
1692
|
|
|
1195
1693
|
detector.onBeat.add(({ audioTime, tempo, isDownbeat, energy }) => {
|
|
1196
|
-
|
|
1197
|
-
|
|
1694
|
+
sprite.scale.set(1.5);
|
|
1695
|
+
new Tween().target(sprite.scale).to({ x: 1, y: 1 }).duration(200).start();
|
|
1198
1696
|
});
|
|
1199
1697
|
|
|
1200
1698
|
detector.onDownbeat.add(() => {
|
|
1201
|
-
|
|
1699
|
+
boss.attack(); // syncs exactly to "the 1" of each bar
|
|
1202
1700
|
});
|
|
1203
1701
|
```
|
|
1204
1702
|
|
|
@@ -1367,22 +1865,22 @@ breaking change.
|
|
|
1367
1865
|
|
|
1368
1866
|
```ts
|
|
1369
1867
|
// Before:
|
|
1370
|
-
sound.play();
|
|
1371
|
-
sound.playPooled();
|
|
1868
|
+
sound.play(); // singleton — second call replaces first
|
|
1869
|
+
sound.playPooled(); // multi-instance — concurrent plays
|
|
1372
1870
|
|
|
1373
1871
|
// After:
|
|
1374
|
-
sound.play();
|
|
1872
|
+
sound.play(); // multi-instance — concurrent plays (default!)
|
|
1375
1873
|
sound.play({ replace: true }); // singleton — equivalent of old play()
|
|
1376
1874
|
```
|
|
1377
1875
|
|
|
1378
1876
|
```ts
|
|
1379
1877
|
// Before — direct destination routing was implicit:
|
|
1380
1878
|
const sound = new Sound(buffer);
|
|
1381
|
-
sound.play();
|
|
1879
|
+
sound.play(); // → audioContext.destination
|
|
1382
1880
|
|
|
1383
1881
|
// After — routes through the soundBus by default:
|
|
1384
1882
|
const sound = new Sound(buffer);
|
|
1385
|
-
sound.play();
|
|
1883
|
+
sound.play(); // → app.audio.sound → app.audio.master → destination
|
|
1386
1884
|
|
|
1387
1885
|
// Override to a custom bus:
|
|
1388
1886
|
const dialogueBus = new AudioBus('dialogue', { parent: app.audio.master });
|
|
@@ -1393,8 +1891,8 @@ sound.bus = dialogueBus;
|
|
|
1393
1891
|
```ts
|
|
1394
1892
|
// Spatial audio:
|
|
1395
1893
|
const explosion = new Sound(buffer);
|
|
1396
|
-
explosion.position = { x: 200, y: 100 };
|
|
1397
|
-
app.audio.listener.target = playerSprite;
|
|
1894
|
+
explosion.position = { x: 200, y: 100 }; // becomes spatial
|
|
1895
|
+
app.audio.listener.target = playerSprite; // ears follow player
|
|
1398
1896
|
|
|
1399
1897
|
explosion.play();
|
|
1400
1898
|
// → routes through equalpower panner with distance falloff
|
|
@@ -1431,7 +1929,7 @@ infrastructure. Pure additive — no behavior changes for existing code.
|
|
|
1431
1929
|
**world-space position** via `getGlobalTransform()`, so following a
|
|
1432
1930
|
Sprite nested under a translated/rotated Container works correctly.
|
|
1433
1931
|
New exported type `ViewFollowTarget = SceneNode | { x: number; y:
|
|
1434
|
-
|
|
1932
|
+
number } | null`.
|
|
1435
1933
|
- **Audio fade helpers on `AbstractMedia`** — both `Sound` and `Music`
|
|
1436
1934
|
inherit:
|
|
1437
1935
|
- `fadeIn(durationMs): this` — ramps gain from 0 to current volume.
|
|
@@ -1498,7 +1996,7 @@ detection. Pure performance change — no public API surface changes.
|
|
|
1498
1996
|
components. Recomputes only when the sprite's transform or local
|
|
1499
1997
|
bounds change. Previously had a `// todo cache this` comment.
|
|
1500
1998
|
- **`Sprite.getNormals()`** returns a stable `[Vector, Vector,
|
|
1501
|
-
|
|
1999
|
+
Vector, Vector]` array. The four `Vector` instances are reused
|
|
1502
2000
|
across calls; previously each call allocated four new `Vector`s.
|
|
1503
2001
|
Recomputes only when vertices change. Reduces GC pressure in
|
|
1504
2002
|
collision-detection hot paths.
|
|
@@ -1580,7 +2078,7 @@ per-frame application hook.
|
|
|
1580
2078
|
|
|
1581
2079
|
- **`Application.debug` removed** — was added in 0.6.15. Apps that
|
|
1582
2080
|
used `app.debug.show()` must migrate to `import { DebugOverlay }
|
|
1583
|
-
|
|
2081
|
+
from '@codexo/exojs/debug'` and instantiate manually. **Breaking
|
|
1584
2082
|
change**, but the affected window is one day (0.6.15 → 0.6.17).
|
|
1585
2083
|
|
|
1586
2084
|
### Notes
|
|
@@ -1677,7 +2175,7 @@ zero cost when not shown.
|
|
|
1677
2175
|
|
|
1678
2176
|
Reshapes the interaction system around a per-frame tick and adds an
|
|
1679
2177
|
opt-in drag-and-drop helper. The public per-node signal API from 0.6.13
|
|
1680
|
-
is unchanged; only event
|
|
2178
|
+
is unchanged; only event _cadence_ and a new `draggable` flag.
|
|
1681
2179
|
|
|
1682
2180
|
### Added
|
|
1683
2181
|
|
|
@@ -1813,12 +2311,13 @@ existing surface changes shape.
|
|
|
1813
2311
|
properties on any target object:
|
|
1814
2312
|
|
|
1815
2313
|
```ts
|
|
1816
|
-
app.tweens
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
2314
|
+
app.tweens
|
|
2315
|
+
.create(sprite)
|
|
2316
|
+
.to({ x: 100, alpha: 0.5 }, 1.0) // 1 second
|
|
2317
|
+
.easing(Ease.cubicOut)
|
|
2318
|
+
.delay(0.2)
|
|
2319
|
+
.onComplete(() => console.log('done'))
|
|
2320
|
+
.start();
|
|
1822
2321
|
```
|
|
1823
2322
|
|
|
1824
2323
|
Lifecycle: `Idle → Active → Complete | Stopped` (with
|
|
@@ -1830,6 +2329,7 @@ existing surface changes shape.
|
|
|
1830
2329
|
interpolation), `onUpdate` (per frame), `onRepeat` (cycle
|
|
1831
2330
|
boundaries), `onComplete` (final cycle ends naturally).
|
|
1832
2331
|
`stop()` does NOT fire `onComplete`.
|
|
2332
|
+
|
|
1833
2333
|
- **`TweenManager` class.** Owns active tweens and ticks them
|
|
1834
2334
|
from `Application.update()`. Use `app.tweens.create(target)` to
|
|
1835
2335
|
spawn-and-register a tween in one call; `app.tweens.add(tween)`
|
|
@@ -1925,7 +2425,7 @@ one atlas — memory-efficient at scale, single drawcall per Text.
|
|
|
1925
2425
|
|
|
1926
2426
|
- **`DynamicGlyphAtlas`** — public class. Constructor takes
|
|
1927
2427
|
`width = 1024, height = 1024`. Has `getGlyph(char, family, size,
|
|
1928
|
-
|
|
2428
|
+
weight, style) → GlyphInfo` (cached or rasterizes), `clear()` to
|
|
1929
2429
|
reset, and `texture` for binding to a Mesh. Internal shelf
|
|
1930
2430
|
bin-packing; throws on atlas-full (LRU eviction is V2).
|
|
1931
2431
|
- **`layoutText(text, style, atlas)`** — pure function. Returns
|
|
@@ -1937,7 +2437,7 @@ one atlas — memory-efficient at scale, single drawcall per Text.
|
|
|
1937
2437
|
their own atlas / layout pipelines.
|
|
1938
2438
|
- **TextStyle gets `fillColor: Color`** (defaults to white, used
|
|
1939
2439
|
via mesh.tint after glyph rasterization), **`fontStyle: 'normal'
|
|
1940
|
-
|
|
2440
|
+
| 'italic'`**, and **`lineHeight: number`** (multiplied by
|
|
1941
2441
|
fontSize for line spacing, defaults to 1.2). `align` field is
|
|
1942
2442
|
now strongly typed as `TextAlignment`.
|
|
1943
2443
|
|
|
@@ -1967,8 +2467,8 @@ one atlas — memory-efficient at scale, single drawcall per Text.
|
|
|
1967
2467
|
`document.createElement('canvas')` (works in jsdom / older
|
|
1968
2468
|
browsers).
|
|
1969
2469
|
- First-render of a never-seen glyph costs one canvas2d round-trip
|
|
1970
|
-
|
|
1971
|
-
|
|
2470
|
+
- texture re-upload. Cached glyphs are zero-cost on subsequent
|
|
2471
|
+
renders.
|
|
1972
2472
|
- Per-character animation, MSDF rendering, word-wrap, BiDi, and
|
|
1973
2473
|
text outlines / drop-shadows are all V2.
|
|
1974
2474
|
|
|
@@ -2137,7 +2637,7 @@ one unified rendering path for everything triangle-shaped.
|
|
|
2137
2637
|
(`buildLine`, `buildPath`, `buildCircle`, `buildEllipse`,
|
|
2138
2638
|
`buildRectangle`, `buildPolygon`, `buildStar`) now return a
|
|
2139
2639
|
`MeshGeometryData` plain object — `{ vertices: Float32Array,
|
|
2140
|
-
|
|
2640
|
+
indices: Uint16Array, points: Array<number> }` — directly suitable
|
|
2141
2641
|
for `new Mesh({ ... })`.
|
|
2142
2642
|
- **`WebGl2PrimitiveRenderer` and `WebGpuPrimitiveRenderer` removed.**
|
|
2143
2643
|
Their work moved entirely into the existing `*MeshRenderer`s. Both
|
|
@@ -2166,18 +2666,14 @@ one unified rendering path for everything triangle-shaped.
|
|
|
2166
2666
|
// Before (0.6.4)
|
|
2167
2667
|
import { DrawableShape, Geometry, RenderingPrimitives, Color } from '@codexo/exojs';
|
|
2168
2668
|
|
|
2169
|
-
const shape = new DrawableShape(
|
|
2170
|
-
new Geometry({ vertices: [0, 0, 100, 0, 50, 100], indices: [0, 1, 2] }),
|
|
2171
|
-
Color.red,
|
|
2172
|
-
RenderingPrimitives.Triangles,
|
|
2173
|
-
);
|
|
2669
|
+
const shape = new DrawableShape(new Geometry({ vertices: [0, 0, 100, 0, 50, 100], indices: [0, 1, 2] }), Color.red, RenderingPrimitives.Triangles);
|
|
2174
2670
|
|
|
2175
2671
|
// After (0.6.5)
|
|
2176
2672
|
import { Mesh, Color } from '@codexo/exojs';
|
|
2177
2673
|
|
|
2178
2674
|
const mesh = new Mesh({
|
|
2179
|
-
|
|
2180
|
-
|
|
2675
|
+
vertices: new Float32Array([0, 0, 100, 0, 50, 100]),
|
|
2676
|
+
indices: new Uint16Array([0, 1, 2]),
|
|
2181
2677
|
});
|
|
2182
2678
|
mesh.tint = Color.red;
|
|
2183
2679
|
```
|
|
@@ -2253,13 +2749,13 @@ match the rest of ExoJS.
|
|
|
2253
2749
|
```ts
|
|
2254
2750
|
// Before (0.6.3)
|
|
2255
2751
|
import { capabilities, isSupported } from '@codexo/exojs';
|
|
2256
|
-
if (capabilities.webgpu) startWebGpu();
|
|
2752
|
+
if (capabilities.webgpu) startWebGpu(); // false positives possible
|
|
2257
2753
|
if (isSupported('touch')) showTouchUi();
|
|
2258
2754
|
|
|
2259
2755
|
// After (0.6.4)
|
|
2260
2756
|
import { Capabilities } from '@codexo/exojs';
|
|
2261
2757
|
const caps = await Capabilities.ready;
|
|
2262
|
-
if (caps.webgpuAdapter) startWebGpu();
|
|
2758
|
+
if (caps.webgpuAdapter) startWebGpu(); // strict adapter check
|
|
2263
2759
|
if (caps.touch) showTouchUi();
|
|
2264
2760
|
|
|
2265
2761
|
// Or via Application after start:
|
|
@@ -2379,12 +2875,12 @@ and breaks freely between minors.
|
|
|
2379
2875
|
- **`Scene` is class-only; the plain-object definition constructor is
|
|
2380
2876
|
gone.** `new Scene({ update() { ... } })` no longer works. Subclass
|
|
2381
2877
|
to define a scene — `class GameScene extends Scene { override
|
|
2382
|
-
|
|
2383
|
-
|
|
2878
|
+
update(...) { ... } }` for named scenes, `new class extends Scene
|
|
2879
|
+
{ ... }` for one-offs. The `SceneData` interface and
|
|
2384
2880
|
`SceneInstance<T>` type alias are removed (they only existed to
|
|
2385
2881
|
type the spread-into-`this` constructor). Internal Scene fields
|
|
2386
2882
|
move from ECMAScript `#`-private to TS `protected _app/_root/
|
|
2387
|
-
|
|
2883
|
+
_stackMode/_inputMode` — subclasses can now reach internal state
|
|
2388
2884
|
directly when they need to.
|
|
2389
2885
|
- **npm package shape simplified.** Dropped: `dist/exo.global.js` /
|
|
2390
2886
|
`dist/exo.global.min.js` (legacy IIFE for `<script>` use) and
|
|
@@ -2445,33 +2941,49 @@ and breaks freely between minors.
|
|
|
2445
2941
|
```ts
|
|
2446
2942
|
// Before (0.5.x)
|
|
2447
2943
|
class GameScene extends Scene {
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2944
|
+
override draw(runtime: SceneRenderRuntime): void {
|
|
2945
|
+
this.root.render(runtime);
|
|
2946
|
+
}
|
|
2451
2947
|
}
|
|
2452
2948
|
|
|
2453
2949
|
const triangleRenderer = new CustomRenderer(app.renderManager);
|
|
2454
2950
|
|
|
2455
|
-
if (app.renderManager instanceof WebGpuRenderManager) {
|
|
2951
|
+
if (app.renderManager instanceof WebGpuRenderManager) {
|
|
2952
|
+
/* ... */
|
|
2953
|
+
}
|
|
2456
2954
|
|
|
2457
2955
|
// Plain-object scene
|
|
2458
|
-
app.start(
|
|
2956
|
+
app.start(
|
|
2957
|
+
new Scene({
|
|
2958
|
+
update() {
|
|
2959
|
+
/* ... */
|
|
2960
|
+
},
|
|
2961
|
+
}),
|
|
2962
|
+
);
|
|
2459
2963
|
```
|
|
2460
2964
|
|
|
2461
2965
|
```ts
|
|
2462
2966
|
// After (0.6.0)
|
|
2463
2967
|
class GameScene extends Scene {
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2968
|
+
override draw(backend: RenderBackend): void {
|
|
2969
|
+
this.root.render(backend);
|
|
2970
|
+
}
|
|
2467
2971
|
}
|
|
2468
2972
|
|
|
2469
2973
|
const triangleRenderer = new CustomRenderer(app.backend);
|
|
2470
2974
|
|
|
2471
|
-
if (app.backend instanceof WebGpuBackend) {
|
|
2975
|
+
if (app.backend instanceof WebGpuBackend) {
|
|
2976
|
+
/* ... */
|
|
2977
|
+
}
|
|
2472
2978
|
|
|
2473
2979
|
// Anonymous-subclass scene (or named subclass)
|
|
2474
|
-
app.start(
|
|
2980
|
+
app.start(
|
|
2981
|
+
new (class extends Scene {
|
|
2982
|
+
override update() {
|
|
2983
|
+
/* ... */
|
|
2984
|
+
}
|
|
2985
|
+
})(),
|
|
2986
|
+
);
|
|
2475
2987
|
```
|
|
2476
2988
|
|
|
2477
2989
|
## [0.5.1] - 2026-04-28
|
|
@@ -2565,6 +3077,7 @@ Three focused breaking changes targeted at the first pre-1.0 minor: a hierarchy-
|
|
|
2565
3077
|
- `null` — no mask.
|
|
2566
3078
|
|
|
2567
3079
|
Setting `node.mask = node` (self-mask) throws at runtime.
|
|
3080
|
+
|
|
2568
3081
|
- **`SceneRenderRuntime` mask primitives renamed** to match the new vocabulary:
|
|
2569
3082
|
- `pushMask(maskBounds)` / `popMask()` → `pushScissorRect(bounds)` / `popScissorRect()` (lower-level scissor primitive used internally by the `Rectangle` mask path).
|
|
2570
3083
|
- New `composeWithAlphaMask(content, mask, x, y, width, height, blendMode)` — used internally by the Texture/RenderTexture/RenderNode mask paths.
|
|
@@ -2582,19 +3095,19 @@ Three focused breaking changes targeted at the first pre-1.0 minor: a hierarchy-
|
|
|
2582
3095
|
|
|
2583
3096
|
### Migration
|
|
2584
3097
|
|
|
2585
|
-
| Before (0.4.x)
|
|
2586
|
-
|
|
2587
|
-
| `import { Transformable } from '@codexo/exojs'`; `class X extends Transformable`
|
|
2588
|
-
| `import { TransformableFlags } from '@codexo/exojs'`
|
|
2589
|
-
| `node.mask = anyShapeNode`
|
|
2590
|
-
| Want fast axis-aligned clipping?
|
|
2591
|
-
| Want to clip with a texture's alpha channel?
|
|
2592
|
-
| Want a transformed/positioned alpha mask?
|
|
2593
|
-
| `runtime.pushMask(rect)` / `runtime.popMask()`
|
|
2594
|
-
| `class Group extends SceneNode { override render() {...} }`
|
|
2595
|
-
| `class CustomContainer extends Container { override addChild(child: SceneNode) {...} }` | `class CustomContainer extends Container { override addChild(child: RenderNode) {...} }`
|
|
2596
|
-
| `Scene.create({ update() {...} })`
|
|
2597
|
-
| `Scene.create({})`
|
|
3098
|
+
| Before (0.4.x) | After |
|
|
3099
|
+
| --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
|
3100
|
+
| `import { Transformable } from '@codexo/exojs'`; `class X extends Transformable` | `import { SceneNode } from '@codexo/exojs'`; `class X extends SceneNode` |
|
|
3101
|
+
| `import { TransformableFlags } from '@codexo/exojs'` | Internal flag enum is no longer public; use SceneNode's high-level transform accessors instead. |
|
|
3102
|
+
| `node.mask = anyShapeNode` _(silently clipped to bounding rect)_ | `node.mask = anyShapeNode` _(now a real shape mask via alpha compositing — except bare SceneNode which is rejected at compile time)_ |
|
|
3103
|
+
| Want fast axis-aligned clipping? | `node.mask = new Rectangle(x, y, w, h)` |
|
|
3104
|
+
| Want to clip with a texture's alpha channel? | `node.mask = texture` or `node.mask = renderTexture` |
|
|
3105
|
+
| Want a transformed/positioned alpha mask? | `node.mask = new Sprite(texture)` (Sprite's transform/position/scale apply to the mask source) |
|
|
3106
|
+
| `runtime.pushMask(rect)` / `runtime.popMask()` | `runtime.pushScissorRect(rect)` / `runtime.popScissorRect()` (renamed; behavior unchanged) |
|
|
3107
|
+
| `class Group extends SceneNode { override render() {...} }` | `class Group extends RenderNode { override render() {...} }` |
|
|
3108
|
+
| `class CustomContainer extends Container { override addChild(child: SceneNode) {...} }` | `class CustomContainer extends Container { override addChild(child: RenderNode) {...} }` |
|
|
3109
|
+
| `Scene.create({ update() {...} })` | `new Scene({ update() {...} })` (drop-in replacement; same `this` typing via `ThisType<Scene & T>`) |
|
|
3110
|
+
| `Scene.create({})` | `new Scene()` |
|
|
2598
3111
|
|
|
2599
3112
|
No deprecated aliases are provided. The migration is mechanical and the project is pre-1.0 with explicit "may break between minors" policy.
|
|
2600
3113
|
|