@codexo/exojs 0.8.0 → 0.8.3
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 -208
- 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 +37 -1
- package/dist/esm/audio/AudioAnalyser.js +150 -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 +66 -4
- package/dist/esm/audio/BeatDetector.js +145 -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/mel.js +70 -0
- package/dist/esm/audio/dsp/mel.js.map +1 -0
- 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/RenderPassInspectorLayer.d.ts +71 -0
- package/dist/esm/debug/RenderPassInspectorLayer.js +201 -0
- package/dist/esm/debug/RenderPassInspectorLayer.js.map +1 -0
- package/dist/esm/debug/index.d.ts +4 -3
- package/dist/esm/debug/index.js +4 -3
- package/dist/esm/debug/index.js.map +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +101 -98
- 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 +9 -9
- package/dist/esm/particles/ParticleSystem.js +29 -29
- package/dist/esm/particles/ParticleSystem.js.map +1 -1
- package/dist/esm/particles/distributions/BoxArea.js.map +1 -1
- package/dist/esm/particles/distributions/CircleArea.js.map +1 -1
- package/dist/esm/particles/distributions/ConeDirection.js.map +1 -1
- package/dist/esm/particles/distributions/Constant.js.map +1 -1
- package/dist/esm/particles/distributions/Curve.d.ts +1 -1
- package/dist/esm/particles/distributions/Curve.js.map +1 -1
- package/dist/esm/particles/distributions/Gradient.d.ts +1 -1
- package/dist/esm/particles/distributions/Gradient.js +1 -1
- package/dist/esm/particles/distributions/Gradient.js.map +1 -1
- package/dist/esm/particles/distributions/LineSegment.js.map +1 -1
- package/dist/esm/particles/distributions/Range.js.map +1 -1
- package/dist/esm/particles/distributions/VectorRange.js.map +1 -1
- package/dist/esm/particles/distributions/index.d.ts +9 -9
- package/dist/esm/particles/gpu/ParticleGpuState.d.ts +3 -3
- package/dist/esm/particles/gpu/ParticleGpuState.js +48 -21
- package/dist/esm/particles/gpu/ParticleGpuState.js.map +1 -1
- package/dist/esm/particles/index.d.ts +1 -1
- package/dist/esm/particles/modules/AlphaFadeOverLifetime.d.ts +2 -2
- package/dist/esm/particles/modules/AlphaFadeOverLifetime.js.map +1 -1
- package/dist/esm/particles/modules/ApplyForce.d.ts +1 -1
- package/dist/esm/particles/modules/ApplyForce.js.map +1 -1
- package/dist/esm/particles/modules/AttractToPoint.d.ts +1 -1
- package/dist/esm/particles/modules/AttractToPoint.js.map +1 -1
- package/dist/esm/particles/modules/BurstSpawn.d.ts +4 -4
- package/dist/esm/particles/modules/BurstSpawn.js +2 -2
- package/dist/esm/particles/modules/BurstSpawn.js.map +1 -1
- package/dist/esm/particles/modules/ColorOverLifetime.d.ts +2 -2
- package/dist/esm/particles/modules/ColorOverLifetime.js +1 -1
- package/dist/esm/particles/modules/ColorOverLifetime.js.map +1 -1
- package/dist/esm/particles/modules/ColorOverSpeed.d.ts +2 -2
- package/dist/esm/particles/modules/ColorOverSpeed.js +1 -1
- package/dist/esm/particles/modules/ColorOverSpeed.js.map +1 -1
- package/dist/esm/particles/modules/DeathModule.js.map +1 -1
- package/dist/esm/particles/modules/Drag.d.ts +1 -1
- package/dist/esm/particles/modules/Drag.js +1 -3
- package/dist/esm/particles/modules/Drag.js.map +1 -1
- package/dist/esm/particles/modules/OrbitalForce.d.ts +1 -1
- package/dist/esm/particles/modules/OrbitalForce.js.map +1 -1
- package/dist/esm/particles/modules/RateSpawn.d.ts +3 -3
- package/dist/esm/particles/modules/RateSpawn.js +2 -2
- package/dist/esm/particles/modules/RateSpawn.js.map +1 -1
- package/dist/esm/particles/modules/RepelFromPoint.d.ts +1 -1
- package/dist/esm/particles/modules/RepelFromPoint.js.map +1 -1
- package/dist/esm/particles/modules/RotateOverLifetime.d.ts +1 -1
- package/dist/esm/particles/modules/RotateOverLifetime.js +1 -3
- package/dist/esm/particles/modules/RotateOverLifetime.js.map +1 -1
- package/dist/esm/particles/modules/ScaleOverLifetime.d.ts +2 -2
- package/dist/esm/particles/modules/ScaleOverLifetime.js.map +1 -1
- package/dist/esm/particles/modules/SpawnModule.js.map +1 -1
- package/dist/esm/particles/modules/SpawnOnDeath.d.ts +1 -1
- package/dist/esm/particles/modules/SpawnOnDeath.js.map +1 -1
- package/dist/esm/particles/modules/Turbulence.d.ts +1 -1
- package/dist/esm/particles/modules/Turbulence.js.map +1 -1
- package/dist/esm/particles/modules/UpdateModule.js.map +1 -1
- package/dist/esm/particles/modules/VelocityOverLifetime.d.ts +2 -2
- package/dist/esm/particles/modules/VelocityOverLifetime.js.map +1 -1
- package/dist/esm/particles/modules/WgslContribution.d.ts +3 -3
- package/dist/esm/particles/modules/WgslContribution.js.map +1 -1
- package/dist/esm/particles/modules/index.d.ts +17 -17
- 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 +16 -11
- package/dist/esm/rendering/filters/WebGpuShaderFilter.js.map +1 -1
- package/dist/esm/rendering/index.d.ts +30 -27
- package/dist/esm/rendering/mesh/Mesh.d.ts +6 -1
- package/dist/esm/rendering/mesh/Mesh.js +3 -1
- package/dist/esm/rendering/mesh/Mesh.js.map +1 -1
- package/dist/esm/rendering/mesh/MeshShader.d.ts +183 -0
- package/dist/esm/rendering/mesh/MeshShader.js +231 -0
- package/dist/esm/rendering/mesh/MeshShader.js.map +1 -0
- 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/DataTexture.d.ts +115 -0
- package/dist/esm/rendering/texture/DataTexture.js +173 -0
- package/dist/esm/rendering/texture/DataTexture.js.map +1 -0
- 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 +61 -22
- 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 +121 -43
- package/dist/esm/rendering/webgl2/WebGl2MeshRenderer.js.map +1 -1
- package/dist/esm/rendering/webgl2/WebGl2ParticleRenderer.d.ts +1 -1
- package/dist/esm/rendering/webgl2/WebGl2ParticleRenderer.js +10 -18
- 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/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 +8 -7
- package/dist/esm/rendering/webgpu/WebGpuBackend.js +104 -37
- 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 +3 -4
- package/dist/esm/rendering/webgpu/WebGpuMaskCompositor.js.map +1 -1
- package/dist/esm/rendering/webgpu/WebGpuMeshRenderer.d.ts +15 -2
- package/dist/esm/rendering/webgpu/WebGpuMeshRenderer.js +657 -102
- package/dist/esm/rendering/webgpu/WebGpuMeshRenderer.js.map +1 -1
- package/dist/esm/rendering/webgpu/WebGpuParticleRenderer.d.ts +1 -1
- package/dist/esm/rendering/webgpu/WebGpuParticleRenderer.js +100 -56
- 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 +1 -1
- package/dist/esm/rendering/webgpu/compute/index.d.ts +2 -2
- 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 +26260 -24256
- package/dist/exo.esm.js.map +1 -1
- package/package.json +15 -4
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Matrix } from '../../math/Matrix.js';
|
|
2
|
-
import { AbstractWebGpuRenderer } from './AbstractWebGpuRenderer.js';
|
|
3
|
-
import { BlendModes } from '../types.js';
|
|
4
2
|
import { Texture } from '../texture/Texture.js';
|
|
3
|
+
import { BlendModes } from '../types.js';
|
|
4
|
+
import { AbstractWebGpuRenderer } from './AbstractWebGpuRenderer.js';
|
|
5
5
|
import { getWebGpuBlendState } from './WebGpuBlendState.js';
|
|
6
6
|
|
|
7
7
|
/// <reference types="@webgpu/types" />
|
|
@@ -48,16 +48,27 @@ fn fragmentMain(input: VertexOutput) -> @location(0) vec4<f32> {
|
|
|
48
48
|
}
|
|
49
49
|
`;
|
|
50
50
|
// Per-vertex layout (20 bytes): pos f32x2 + uv f32x2 + color u8x4-norm.
|
|
51
|
-
//
|
|
52
|
-
// shader stays branchless and uniform-free except for the per-mesh tint.
|
|
51
|
+
// Default-shader path bakes the (view * globalTransform) into position so the
|
|
52
|
+
// vertex shader stays branchless and uniform-free except for the per-mesh tint.
|
|
53
|
+
// Custom-shader path keeps positions in LOCAL space — the user's vertex
|
|
54
|
+
// shader receives mesh transforms via the auto-bound u_mesh uniform block.
|
|
53
55
|
const vertexStrideBytes = 20;
|
|
54
56
|
const wordsPerVertex = vertexStrideBytes / 4;
|
|
55
57
|
const tintByteLength = 32; // vec4 tint + vec4 flags (only flags.x used)
|
|
58
|
+
// Custom-shader uniform layout:
|
|
59
|
+
// mat3x3<f32> projection — 48 bytes (3 vec3 columns padded to vec4 in WGSL)
|
|
60
|
+
// mat3x3<f32> translation — 48 bytes
|
|
61
|
+
// vec4<f32> tint — 16 bytes
|
|
62
|
+
// Total: 112 bytes; aligned up to 256 for dynamic offset.
|
|
63
|
+
const customMeshUniformBytes = 112;
|
|
64
|
+
const meshUniformAlignment = 256;
|
|
65
|
+
const maxCustomTextureSlots = 7; // user texture uniforms; group 2 binding 1..N
|
|
56
66
|
class WebGpuMeshRenderer extends AbstractWebGpuRenderer {
|
|
57
67
|
_combinedTransform = new Matrix();
|
|
58
68
|
_drawCalls = [];
|
|
59
69
|
_pipelines = new Map();
|
|
60
70
|
_textureBindGroups = new Map();
|
|
71
|
+
_customShaders = new Map();
|
|
61
72
|
_device = null;
|
|
62
73
|
_shaderModule = null;
|
|
63
74
|
_uniformBindGroupLayout = null;
|
|
@@ -81,6 +92,10 @@ class WebGpuMeshRenderer extends AbstractWebGpuRenderer {
|
|
|
81
92
|
if (backend === null) {
|
|
82
93
|
throw new Error('WebGpuMeshRenderer is not connected to a backend.');
|
|
83
94
|
}
|
|
95
|
+
const customShader = mesh.shader;
|
|
96
|
+
if (customShader !== null && customShader.wgsl === null) {
|
|
97
|
+
throw new Error('MeshShader has no `wgsl` source; cannot render through the WebGPU backend.');
|
|
98
|
+
}
|
|
84
99
|
const vertexCount = mesh.vertexCount;
|
|
85
100
|
if (vertexCount === 0) {
|
|
86
101
|
return;
|
|
@@ -88,15 +103,26 @@ class WebGpuMeshRenderer extends AbstractWebGpuRenderer {
|
|
|
88
103
|
const blendMode = mesh.blendMode;
|
|
89
104
|
backend.setBlendMode(blendMode);
|
|
90
105
|
const meshTexture = mesh.texture ?? Texture.white;
|
|
91
|
-
//
|
|
92
|
-
//
|
|
106
|
+
// backend.shouldPremultiplyTextureSample expects RenderTexture-or-Texture.
|
|
107
|
+
// Both branches are valid here. Premultiply flag is ignored by custom
|
|
108
|
+
// shaders (they handle premultiplication themselves), but we still record
|
|
109
|
+
// it so the default path uses the right value.
|
|
93
110
|
const premultiplySample = backend.shouldPremultiplyTextureSample(meshTexture);
|
|
94
111
|
const indexCount = mesh.indexCount;
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
112
|
+
let customDrawIndex = -1;
|
|
113
|
+
if (customShader !== null) {
|
|
114
|
+
const resources = this._getOrCreateCustomShaderResources(customShader);
|
|
115
|
+
customDrawIndex = resources.drawCount;
|
|
116
|
+
resources.drawCount++;
|
|
117
|
+
resources.totalVertices += vertexCount;
|
|
118
|
+
resources.totalIndices += indexCount;
|
|
119
|
+
}
|
|
120
|
+
// Plan offsets within the shared (default) or per-shader (custom) buffers;
|
|
121
|
+
// actual data packing happens in flush() after all drawcalls are known so
|
|
122
|
+
// a single writeBuffer per resource covers the whole frame.
|
|
98
123
|
const drawCall = {
|
|
99
124
|
mesh,
|
|
125
|
+
customShader,
|
|
100
126
|
blendMode,
|
|
101
127
|
texture: meshTexture,
|
|
102
128
|
premultiplySample,
|
|
@@ -104,6 +130,7 @@ class WebGpuMeshRenderer extends AbstractWebGpuRenderer {
|
|
|
104
130
|
vertexCount,
|
|
105
131
|
indexByteOffset: 0,
|
|
106
132
|
indexCount,
|
|
133
|
+
customDrawIndex,
|
|
107
134
|
};
|
|
108
135
|
// Use mutable record (interface readonly is for type safety against
|
|
109
136
|
// callers; the renderer fills these slots in flush()).
|
|
@@ -132,88 +159,194 @@ class WebGpuMeshRenderer extends AbstractWebGpuRenderer {
|
|
|
132
159
|
pass.end();
|
|
133
160
|
backend.submit(encoder.finish());
|
|
134
161
|
}
|
|
135
|
-
this.
|
|
162
|
+
this._resetFrame();
|
|
136
163
|
return;
|
|
137
164
|
}
|
|
138
|
-
// Phase 1: compute layout offsets
|
|
139
|
-
|
|
140
|
-
let
|
|
165
|
+
// Phase 1: compute layout offsets (default vs. custom paths use separate
|
|
166
|
+
// buffers, so default offsets are independent of custom offsets).
|
|
167
|
+
let defaultVertices = 0;
|
|
168
|
+
let defaultIndices = 0;
|
|
169
|
+
const customVertexCursors = new Map(); // running vertex count per shader
|
|
170
|
+
const customIndexCursors = new Map();
|
|
141
171
|
for (let i = 0; i < this._drawCallCount; i++) {
|
|
142
172
|
const dc = this._drawCalls[i];
|
|
143
|
-
dc.
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
173
|
+
if (dc.customShader === null) {
|
|
174
|
+
dc.vertexByteOffset = defaultVertices * vertexStrideBytes;
|
|
175
|
+
dc.indexByteOffset = defaultIndices * Uint16Array.BYTES_PER_ELEMENT;
|
|
176
|
+
defaultVertices += dc.vertexCount;
|
|
177
|
+
defaultIndices += dc.indexCount;
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
const vCursor = customVertexCursors.get(dc.customShader) ?? 0;
|
|
181
|
+
const iCursor = customIndexCursors.get(dc.customShader) ?? 0;
|
|
182
|
+
dc.vertexByteOffset = vCursor * vertexStrideBytes;
|
|
183
|
+
dc.indexByteOffset = iCursor * Uint16Array.BYTES_PER_ELEMENT;
|
|
184
|
+
customVertexCursors.set(dc.customShader, vCursor + dc.vertexCount);
|
|
185
|
+
customIndexCursors.set(dc.customShader, iCursor + dc.indexCount);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// Phase 2: ensure capacities for the totals (default path).
|
|
189
|
+
this._ensureVertexCapacity(defaultVertices);
|
|
190
|
+
this._ensureIndexCapacity(defaultIndices);
|
|
191
|
+
// Default-path uniform buffer holds (tint vec4 + flags vec4) per draw call;
|
|
192
|
+
// each custom-shader resource manages its own.
|
|
193
|
+
const defaultDrawCalls = this._drawCallCount - this._totalCustomDraws();
|
|
194
|
+
this._ensureUniformCapacity(defaultDrawCalls);
|
|
195
|
+
// Phase 3: pack default-path vertex/index/uniform data.
|
|
196
|
+
const defaultUniformBytes = defaultDrawCalls * this._uniformAlignment;
|
|
197
|
+
const defaultUniformData = defaultUniformBytes > 0 ? new ArrayBuffer(defaultUniformBytes) : null;
|
|
198
|
+
const defaultUniformF32 = defaultUniformData !== null ? new Float32Array(defaultUniformData) : null;
|
|
199
|
+
let defaultUniformIndex = 0;
|
|
156
200
|
for (let i = 0; i < this._drawCallCount; i++) {
|
|
157
201
|
const dc = this._drawCalls[i];
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
this.
|
|
202
|
+
if (dc.customShader === null) {
|
|
203
|
+
// Default path: CPU-bake transform into vertex positions.
|
|
204
|
+
this._writeMeshVertices(backend, dc.mesh, dc.vertexByteOffset / vertexStrideBytes, /* bake */ true);
|
|
205
|
+
if (dc.mesh.indices !== null) {
|
|
206
|
+
this._packedIndexData.set(dc.mesh.indices, dc.indexByteOffset / Uint16Array.BYTES_PER_ELEMENT);
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
const start = dc.indexByteOffset / Uint16Array.BYTES_PER_ELEMENT;
|
|
210
|
+
for (let j = 0; j < dc.indexCount; j++) {
|
|
211
|
+
this._packedIndexData[start + j] = j;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
// Pack tint+flags for default path.
|
|
215
|
+
if (defaultUniformF32 !== null) {
|
|
216
|
+
const offsetWords = (defaultUniformIndex * this._uniformAlignment) / Float32Array.BYTES_PER_ELEMENT;
|
|
217
|
+
const tint = dc.mesh.tint;
|
|
218
|
+
defaultUniformF32[offsetWords + 0] = tint.red;
|
|
219
|
+
defaultUniformF32[offsetWords + 1] = tint.green;
|
|
220
|
+
defaultUniformF32[offsetWords + 2] = tint.blue;
|
|
221
|
+
defaultUniformF32[offsetWords + 3] = tint.alpha;
|
|
222
|
+
defaultUniformF32[offsetWords + 4] = dc.premultiplySample ? 1 : 0;
|
|
223
|
+
defaultUniformF32[offsetWords + 5] = 0;
|
|
224
|
+
defaultUniformF32[offsetWords + 6] = 0;
|
|
225
|
+
defaultUniformF32[offsetWords + 7] = 0;
|
|
226
|
+
}
|
|
227
|
+
defaultUniformIndex++;
|
|
161
228
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
229
|
+
}
|
|
230
|
+
// Phase 3b: pack custom-path vertex/index/uniform data per shader.
|
|
231
|
+
for (const [shader, resources] of this._customShaders) {
|
|
232
|
+
if (resources.drawCount === 0) {
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
this._ensureCustomCapacities(resources);
|
|
236
|
+
// Pack vertices/indices in local space (no CPU bake).
|
|
237
|
+
let vWritten = 0;
|
|
238
|
+
let iWritten = 0;
|
|
239
|
+
let drawCursor = 0;
|
|
240
|
+
for (let i = 0; i < this._drawCallCount; i++) {
|
|
241
|
+
const dc = this._drawCalls[i];
|
|
242
|
+
if (dc.customShader !== shader)
|
|
243
|
+
continue;
|
|
244
|
+
this._writeMeshVerticesIntoBuffer(dc.mesh, vWritten, resources.vertexFloatView, resources.vertexUintView);
|
|
245
|
+
if (dc.mesh.indices !== null) {
|
|
246
|
+
resources.indexData.set(dc.mesh.indices, iWritten);
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
for (let j = 0; j < dc.indexCount; j++) {
|
|
250
|
+
resources.indexData[iWritten + j] = j;
|
|
251
|
+
}
|
|
166
252
|
}
|
|
253
|
+
// Write mesh-uniform slot (proj/trans/tint) with dynamic offset.
|
|
254
|
+
this._writeCustomMeshUniform(shader, resources, drawCursor, dc.mesh, backend);
|
|
255
|
+
vWritten += dc.vertexCount;
|
|
256
|
+
iWritten += dc.indexCount;
|
|
257
|
+
drawCursor++;
|
|
167
258
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
//
|
|
184
|
-
const encoder = device.createCommandEncoder();
|
|
259
|
+
device.queue.writeBuffer(resources.vertexBuffer, 0, resources.vertexData, 0, resources.totalVertices * vertexStrideBytes);
|
|
260
|
+
device.queue.writeBuffer(resources.indexBuffer, 0, resources.indexData.buffer, resources.indexData.byteOffset, resources.totalIndices * Uint16Array.BYTES_PER_ELEMENT);
|
|
261
|
+
// Build/refresh user uniform UBO from shader.uniforms (re-built every
|
|
262
|
+
// frame so mutations to shader.uniforms.X are picked up).
|
|
263
|
+
this._uploadUserUniforms(shader, resources);
|
|
264
|
+
}
|
|
265
|
+
// Phase 4: single writeBuffer per resource for the default path.
|
|
266
|
+
if (defaultVertices > 0) {
|
|
267
|
+
device.queue.writeBuffer(this._vertexBuffer, 0, this._vertexData, 0, defaultVertices * vertexStrideBytes);
|
|
268
|
+
device.queue.writeBuffer(this._indexBuffer, 0, this._packedIndexData.buffer, this._packedIndexData.byteOffset, defaultIndices * Uint16Array.BYTES_PER_ELEMENT);
|
|
269
|
+
}
|
|
270
|
+
if (defaultUniformData !== null) {
|
|
271
|
+
device.queue.writeBuffer(this._uniformBuffer, 0, defaultUniformData, 0, defaultUniformBytes);
|
|
272
|
+
}
|
|
273
|
+
// Phase 5: single render pass with one drawIndexed per mesh, switching
|
|
274
|
+
// pipeline+bind groups between default and custom paths as needed.
|
|
275
|
+
const encoder = device.createCommandEncoder({ label: 'WebGpuMeshRenderer' });
|
|
185
276
|
const pass = encoder.beginRenderPass({
|
|
186
277
|
colorAttachments: [backend.createColorAttachment()],
|
|
278
|
+
label: 'WebGpuMeshRenderer pass',
|
|
187
279
|
});
|
|
188
280
|
backend.stats.renderPasses++;
|
|
189
281
|
if (scissor !== null) {
|
|
190
282
|
pass.setScissorRect(scissor.x, scissor.y, scissor.width, scissor.height);
|
|
191
283
|
}
|
|
284
|
+
const renderTargetFormat = backend.renderTargetFormat;
|
|
285
|
+
let lastShader = null;
|
|
192
286
|
let lastBlendMode = null;
|
|
193
287
|
let lastFormat = null;
|
|
194
288
|
let lastTexture = null;
|
|
195
|
-
|
|
289
|
+
let defaultDrawCursor = 0;
|
|
290
|
+
const customDrawCursors = new Map();
|
|
196
291
|
for (let i = 0; i < this._drawCallCount; i++) {
|
|
197
292
|
const dc = this._drawCalls[i];
|
|
198
|
-
if (dc.
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
293
|
+
if (dc.customShader === null) {
|
|
294
|
+
// ----- Default path -----
|
|
295
|
+
const needsPipeline = lastShader !== 'default' || dc.blendMode !== lastBlendMode || renderTargetFormat !== lastFormat;
|
|
296
|
+
if (needsPipeline) {
|
|
297
|
+
pass.setPipeline(this._getPipeline({ blendMode: dc.blendMode, format: renderTargetFormat }));
|
|
298
|
+
lastShader = 'default';
|
|
299
|
+
lastBlendMode = dc.blendMode;
|
|
300
|
+
lastFormat = renderTargetFormat;
|
|
301
|
+
// Pipeline switch invalidates bind group state assumptions.
|
|
302
|
+
lastTexture = null;
|
|
303
|
+
}
|
|
304
|
+
pass.setBindGroup(0, this._uniformBindGroup, [defaultDrawCursor * this._uniformAlignment]);
|
|
305
|
+
if (dc.texture !== lastTexture) {
|
|
306
|
+
lastTexture = dc.texture;
|
|
307
|
+
pass.setBindGroup(1, this._getTextureBindGroup(backend, dc.texture));
|
|
308
|
+
}
|
|
309
|
+
pass.setVertexBuffer(0, this._vertexBuffer, dc.vertexByteOffset);
|
|
310
|
+
pass.setIndexBuffer(this._indexBuffer, 'uint16', dc.indexByteOffset);
|
|
311
|
+
pass.drawIndexed(dc.indexCount);
|
|
312
|
+
defaultDrawCursor++;
|
|
202
313
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
314
|
+
else {
|
|
315
|
+
// ----- Custom path -----
|
|
316
|
+
const resources = this._customShaders.get(dc.customShader);
|
|
317
|
+
const needsPipeline = lastShader !== dc.customShader || dc.blendMode !== lastBlendMode || renderTargetFormat !== lastFormat;
|
|
318
|
+
// Wrap each custom-shader draw in a debug group so capture tools
|
|
319
|
+
// (Spector.js, Chrome DevTools' WebGPU panel) show meaningful
|
|
320
|
+
// labels for the otherwise-anonymous mesh draws inside the
|
|
321
|
+
// batched render pass.
|
|
322
|
+
pass.pushDebugGroup('MeshShader (custom)');
|
|
323
|
+
if (needsPipeline) {
|
|
324
|
+
pass.setPipeline(this._getOrCreateCustomPipeline(resources, dc.blendMode, renderTargetFormat));
|
|
325
|
+
lastShader = dc.customShader;
|
|
326
|
+
lastBlendMode = dc.blendMode;
|
|
327
|
+
lastFormat = renderTargetFormat;
|
|
328
|
+
lastTexture = null;
|
|
329
|
+
// User bind group is shader-scoped; rebind once per shader switch.
|
|
330
|
+
pass.setBindGroup(2, this._buildUserBindGroup(backend, dc.customShader, resources));
|
|
331
|
+
}
|
|
332
|
+
const cursor = customDrawCursors.get(dc.customShader) ?? 0;
|
|
333
|
+
pass.setBindGroup(0, resources.meshUniformBindGroup, [cursor * meshUniformAlignment]);
|
|
334
|
+
if (dc.texture !== lastTexture) {
|
|
335
|
+
lastTexture = dc.texture;
|
|
336
|
+
pass.setBindGroup(1, this._getOrCreateCustomMeshTextureBindGroup(resources, backend, dc.texture));
|
|
337
|
+
}
|
|
338
|
+
pass.setVertexBuffer(0, resources.vertexBuffer, dc.vertexByteOffset);
|
|
339
|
+
pass.setIndexBuffer(resources.indexBuffer, 'uint16', dc.indexByteOffset);
|
|
340
|
+
pass.drawIndexed(dc.indexCount);
|
|
341
|
+
pass.popDebugGroup();
|
|
342
|
+
customDrawCursors.set(dc.customShader, cursor + 1);
|
|
207
343
|
}
|
|
208
|
-
pass.setVertexBuffer(0, this._vertexBuffer, dc.vertexByteOffset);
|
|
209
|
-
pass.setIndexBuffer(this._indexBuffer, 'uint16', dc.indexByteOffset);
|
|
210
|
-
pass.drawIndexed(dc.indexCount);
|
|
211
344
|
backend.stats.batches++;
|
|
212
345
|
backend.stats.drawCalls++;
|
|
213
346
|
}
|
|
214
347
|
pass.end();
|
|
215
348
|
backend.submit(encoder.finish());
|
|
216
|
-
this.
|
|
349
|
+
this._resetFrame();
|
|
217
350
|
}
|
|
218
351
|
destroy() {
|
|
219
352
|
this.disconnect();
|
|
@@ -227,21 +360,14 @@ class WebGpuMeshRenderer extends AbstractWebGpuRenderer {
|
|
|
227
360
|
if (typeof device.createRenderPipelineAsync !== 'function') {
|
|
228
361
|
return;
|
|
229
362
|
}
|
|
230
|
-
const blendModes = [
|
|
231
|
-
BlendModes.Normal,
|
|
232
|
-
BlendModes.Additive,
|
|
233
|
-
BlendModes.Subtract,
|
|
234
|
-
BlendModes.Multiply,
|
|
235
|
-
BlendModes.Screen,
|
|
236
|
-
];
|
|
363
|
+
const blendModes = [BlendModes.Normal, BlendModes.Additive, BlendModes.Subtract, BlendModes.Multiply, BlendModes.Screen];
|
|
237
364
|
const promises = [];
|
|
238
365
|
for (const blendMode of blendModes) {
|
|
239
366
|
for (const format of formats) {
|
|
240
367
|
const key = `${blendMode}:${format}`;
|
|
241
368
|
if (this._pipelines.has(key))
|
|
242
369
|
continue;
|
|
243
|
-
promises.push(device.createRenderPipelineAsync(this._buildPipelineDescriptor(blendMode, format))
|
|
244
|
-
.then((pipeline) => {
|
|
370
|
+
promises.push(device.createRenderPipelineAsync(this._buildPipelineDescriptor(blendMode, format)).then(pipeline => {
|
|
245
371
|
this._pipelines.set(key, pipeline);
|
|
246
372
|
}));
|
|
247
373
|
}
|
|
@@ -255,11 +381,13 @@ class WebGpuMeshRenderer extends AbstractWebGpuRenderer {
|
|
|
255
381
|
this._device = backend.device;
|
|
256
382
|
this._shaderModule = this._device.createShaderModule({ code: meshShaderSource });
|
|
257
383
|
this._uniformBindGroupLayout = this._device.createBindGroupLayout({
|
|
258
|
-
entries: [
|
|
384
|
+
entries: [
|
|
385
|
+
{
|
|
259
386
|
binding: 0,
|
|
260
387
|
visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
|
|
261
388
|
buffer: { type: 'uniform', hasDynamicOffset: true },
|
|
262
|
-
}
|
|
389
|
+
},
|
|
390
|
+
],
|
|
263
391
|
});
|
|
264
392
|
this._textureBindGroupLayout = this._device.createBindGroupLayout({
|
|
265
393
|
entries: [
|
|
@@ -294,6 +422,15 @@ class WebGpuMeshRenderer extends AbstractWebGpuRenderer {
|
|
|
294
422
|
this._textureBindGroupLayout = null;
|
|
295
423
|
this._uniformBindGroupLayout = null;
|
|
296
424
|
this._shaderModule = null;
|
|
425
|
+
// Custom shaders are owned by user code (one MeshShader can be shared
|
|
426
|
+
// across multiple Mesh instances). Their resources are released when the
|
|
427
|
+
// user calls shader.destroy(), which fires our _onDispose callback. On
|
|
428
|
+
// backend disconnect we eagerly release everything to avoid GPU leaks
|
|
429
|
+
// even if the user keeps the shader reference around.
|
|
430
|
+
for (const resources of this._customShaders.values()) {
|
|
431
|
+
this._releaseCustomShaderResources(resources);
|
|
432
|
+
}
|
|
433
|
+
this._customShaders.clear();
|
|
297
434
|
this._device = null;
|
|
298
435
|
this._backend = null;
|
|
299
436
|
this._drawCallCount = 0;
|
|
@@ -301,32 +438,47 @@ class WebGpuMeshRenderer extends AbstractWebGpuRenderer {
|
|
|
301
438
|
this._indexBufferCapacity = 0;
|
|
302
439
|
this._uniformBufferCapacity = 0;
|
|
303
440
|
}
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
.copy(mesh.getGlobalTransform())
|
|
309
|
-
.combine(backend.view.getTransform());
|
|
310
|
-
const a = matrix.a;
|
|
311
|
-
const b = matrix.b;
|
|
312
|
-
const c = matrix.c;
|
|
313
|
-
const d = matrix.d;
|
|
314
|
-
const tx = matrix.x;
|
|
315
|
-
const ty = matrix.y;
|
|
441
|
+
// ---------------------------------------------------------------------------
|
|
442
|
+
// Default-path helpers
|
|
443
|
+
// ---------------------------------------------------------------------------
|
|
444
|
+
_writeMeshVertices(backend, mesh, vertexStart, bake) {
|
|
316
445
|
const vertices = mesh.vertices;
|
|
317
446
|
const uvs = mesh.uvs;
|
|
318
447
|
const colors = mesh.colors;
|
|
319
448
|
const vertexCount = mesh.vertexCount;
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
const
|
|
324
|
-
const
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
449
|
+
if (bake) {
|
|
450
|
+
// Bake (view * globalTransform) into vertex positions on the CPU,
|
|
451
|
+
// matching the primitive renderer's no-uniforms approach.
|
|
452
|
+
const matrix = this._combinedTransform.copy(mesh.getGlobalTransform()).combine(backend.view.getTransform());
|
|
453
|
+
const a = matrix.a;
|
|
454
|
+
const b = matrix.b;
|
|
455
|
+
const c = matrix.c;
|
|
456
|
+
const d = matrix.d;
|
|
457
|
+
const tx = matrix.x;
|
|
458
|
+
const ty = matrix.y;
|
|
459
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
460
|
+
const sourceIndex = i * 2;
|
|
461
|
+
const targetIndex = (vertexStart + i) * wordsPerVertex;
|
|
462
|
+
const px = vertices[sourceIndex];
|
|
463
|
+
const py = vertices[sourceIndex + 1];
|
|
464
|
+
this._float32View[targetIndex + 0] = a * px + b * py + tx;
|
|
465
|
+
this._float32View[targetIndex + 1] = c * px + d * py + ty;
|
|
466
|
+
this._float32View[targetIndex + 2] = uvs !== null ? uvs[sourceIndex] : 0;
|
|
467
|
+
this._float32View[targetIndex + 3] = uvs !== null ? uvs[sourceIndex + 1] : 0;
|
|
468
|
+
this._uint32View[targetIndex + 4] = colors !== null ? colors[i] : 0xffffffff;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
else {
|
|
472
|
+
// Should not happen — default path always bakes. Defensive no-op.
|
|
473
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
474
|
+
const sourceIndex = i * 2;
|
|
475
|
+
const targetIndex = (vertexStart + i) * wordsPerVertex;
|
|
476
|
+
this._float32View[targetIndex + 0] = vertices[sourceIndex];
|
|
477
|
+
this._float32View[targetIndex + 1] = vertices[sourceIndex + 1];
|
|
478
|
+
this._float32View[targetIndex + 2] = uvs !== null ? uvs[sourceIndex] : 0;
|
|
479
|
+
this._float32View[targetIndex + 3] = uvs !== null ? uvs[sourceIndex + 1] : 0;
|
|
480
|
+
this._uint32View[targetIndex + 4] = colors !== null ? colors[i] : 0xffffffff;
|
|
481
|
+
}
|
|
330
482
|
}
|
|
331
483
|
}
|
|
332
484
|
_getPipeline(key) {
|
|
@@ -344,7 +496,8 @@ class WebGpuMeshRenderer extends AbstractWebGpuRenderer {
|
|
|
344
496
|
vertex: {
|
|
345
497
|
module: this._shaderModule,
|
|
346
498
|
entryPoint: 'vertexMain',
|
|
347
|
-
buffers: [
|
|
499
|
+
buffers: [
|
|
500
|
+
{
|
|
348
501
|
arrayStride: vertexStrideBytes,
|
|
349
502
|
stepMode: 'vertex',
|
|
350
503
|
attributes: [
|
|
@@ -352,16 +505,19 @@ class WebGpuMeshRenderer extends AbstractWebGpuRenderer {
|
|
|
352
505
|
{ shaderLocation: 1, offset: 8, format: 'float32x2' },
|
|
353
506
|
{ shaderLocation: 2, offset: 16, format: 'unorm8x4' },
|
|
354
507
|
],
|
|
355
|
-
}
|
|
508
|
+
},
|
|
509
|
+
],
|
|
356
510
|
},
|
|
357
511
|
fragment: {
|
|
358
512
|
module: this._shaderModule,
|
|
359
513
|
entryPoint: 'fragmentMain',
|
|
360
|
-
targets: [
|
|
514
|
+
targets: [
|
|
515
|
+
{
|
|
361
516
|
format,
|
|
362
517
|
blend: getWebGpuBlendState(blendMode),
|
|
363
518
|
writeMask: GPUColorWrite.ALL,
|
|
364
|
-
}
|
|
519
|
+
},
|
|
520
|
+
],
|
|
365
521
|
},
|
|
366
522
|
primitive: {
|
|
367
523
|
topology: 'triangle-list',
|
|
@@ -416,6 +572,9 @@ class WebGpuMeshRenderer extends AbstractWebGpuRenderer {
|
|
|
416
572
|
}
|
|
417
573
|
}
|
|
418
574
|
_ensureUniformCapacity(drawCallCount) {
|
|
575
|
+
if (drawCallCount === 0) {
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
419
578
|
const requiredBytes = drawCallCount * this._uniformAlignment;
|
|
420
579
|
if (requiredBytes > this._uniformBufferCapacity) {
|
|
421
580
|
this._uniformBuffer?.destroy();
|
|
@@ -426,13 +585,409 @@ class WebGpuMeshRenderer extends AbstractWebGpuRenderer {
|
|
|
426
585
|
});
|
|
427
586
|
this._uniformBindGroup = this._device.createBindGroup({
|
|
428
587
|
layout: this._uniformBindGroupLayout,
|
|
429
|
-
entries: [
|
|
588
|
+
entries: [
|
|
589
|
+
{
|
|
430
590
|
binding: 0,
|
|
431
591
|
resource: { buffer: this._uniformBuffer, size: tintByteLength },
|
|
432
|
-
}
|
|
592
|
+
},
|
|
593
|
+
],
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
// ---------------------------------------------------------------------------
|
|
598
|
+
// Custom-path helpers
|
|
599
|
+
// ---------------------------------------------------------------------------
|
|
600
|
+
_totalCustomDraws() {
|
|
601
|
+
let total = 0;
|
|
602
|
+
for (const resources of this._customShaders.values()) {
|
|
603
|
+
total += resources.drawCount;
|
|
604
|
+
}
|
|
605
|
+
return total;
|
|
606
|
+
}
|
|
607
|
+
_resetFrame() {
|
|
608
|
+
this._drawCallCount = 0;
|
|
609
|
+
for (const resources of this._customShaders.values()) {
|
|
610
|
+
resources.drawCount = 0;
|
|
611
|
+
resources.totalVertices = 0;
|
|
612
|
+
resources.totalIndices = 0;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
_getOrCreateCustomShaderResources(shader) {
|
|
616
|
+
let resources = this._customShaders.get(shader);
|
|
617
|
+
if (resources !== undefined) {
|
|
618
|
+
return resources;
|
|
619
|
+
}
|
|
620
|
+
if (this._device === null) {
|
|
621
|
+
throw new Error('WebGpuMeshRenderer is not connected to a backend.');
|
|
622
|
+
}
|
|
623
|
+
if (shader.wgsl === null) {
|
|
624
|
+
throw new Error('MeshShader has no `wgsl` source; cannot render through the WebGPU backend.');
|
|
625
|
+
}
|
|
626
|
+
const device = this._device;
|
|
627
|
+
const shaderModule = device.createShaderModule({ code: shader.wgsl });
|
|
628
|
+
const meshUniformLayout = device.createBindGroupLayout({
|
|
629
|
+
entries: [
|
|
630
|
+
{
|
|
631
|
+
binding: 0,
|
|
632
|
+
visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
|
|
633
|
+
buffer: { type: 'uniform', hasDynamicOffset: true },
|
|
634
|
+
},
|
|
635
|
+
],
|
|
636
|
+
});
|
|
637
|
+
const meshTextureLayout = device.createBindGroupLayout({
|
|
638
|
+
entries: [
|
|
639
|
+
{ binding: 0, visibility: GPUShaderStage.FRAGMENT, texture: { sampleType: 'float' } },
|
|
640
|
+
{ binding: 1, visibility: GPUShaderStage.FRAGMENT, sampler: { type: 'filtering' } },
|
|
641
|
+
],
|
|
642
|
+
});
|
|
643
|
+
const userLayout = this._buildUserBindGroupLayout(device, shader);
|
|
644
|
+
const pipelineLayout = device.createPipelineLayout({
|
|
645
|
+
bindGroupLayouts: [meshUniformLayout, meshTextureLayout, userLayout],
|
|
646
|
+
});
|
|
647
|
+
const sampler = device.createSampler({
|
|
648
|
+
magFilter: 'linear',
|
|
649
|
+
minFilter: 'linear',
|
|
650
|
+
addressModeU: 'clamp-to-edge',
|
|
651
|
+
addressModeV: 'clamp-to-edge',
|
|
652
|
+
});
|
|
653
|
+
const initialVertexCount = 64;
|
|
654
|
+
const initialIndexCount = 192;
|
|
655
|
+
const vertexData = new ArrayBuffer(initialVertexCount * vertexStrideBytes);
|
|
656
|
+
resources = {
|
|
657
|
+
shaderModule,
|
|
658
|
+
meshUniformLayout,
|
|
659
|
+
meshTextureLayout,
|
|
660
|
+
userLayout,
|
|
661
|
+
pipelineLayout,
|
|
662
|
+
pipelines: new Map(),
|
|
663
|
+
vertexBuffer: null,
|
|
664
|
+
indexBuffer: null,
|
|
665
|
+
vertexBufferCapacity: 0,
|
|
666
|
+
indexBufferCapacity: 0,
|
|
667
|
+
vertexData,
|
|
668
|
+
vertexFloatView: new Float32Array(vertexData),
|
|
669
|
+
vertexUintView: new Uint32Array(vertexData),
|
|
670
|
+
indexData: new Uint16Array(initialIndexCount),
|
|
671
|
+
meshUniformBuffer: null,
|
|
672
|
+
meshUniformBufferCapacity: 0,
|
|
673
|
+
meshUniformBindGroup: null,
|
|
674
|
+
userUniformBuffer: null,
|
|
675
|
+
userUniformBufferCapacity: 0,
|
|
676
|
+
meshTextureBindGroups: new Map(),
|
|
677
|
+
sampler,
|
|
678
|
+
drawCount: 0,
|
|
679
|
+
totalVertices: 0,
|
|
680
|
+
totalIndices: 0,
|
|
681
|
+
};
|
|
682
|
+
this._customShaders.set(shader, resources);
|
|
683
|
+
// When the user calls shader.destroy(), evict and release.
|
|
684
|
+
shader._onDispose(() => {
|
|
685
|
+
const r = this._customShaders.get(shader);
|
|
686
|
+
if (r !== undefined) {
|
|
687
|
+
this._releaseCustomShaderResources(r);
|
|
688
|
+
this._customShaders.delete(shader);
|
|
689
|
+
}
|
|
690
|
+
});
|
|
691
|
+
return resources;
|
|
692
|
+
}
|
|
693
|
+
_ensureCustomCapacities(resources) {
|
|
694
|
+
const device = this._device;
|
|
695
|
+
// Vertex buffer
|
|
696
|
+
const vertexBytes = resources.totalVertices * vertexStrideBytes;
|
|
697
|
+
if (vertexBytes > resources.vertexData.byteLength) {
|
|
698
|
+
const newSize = Math.max(vertexBytes, resources.vertexData.byteLength * 2);
|
|
699
|
+
resources.vertexData = new ArrayBuffer(newSize);
|
|
700
|
+
resources.vertexFloatView = new Float32Array(resources.vertexData);
|
|
701
|
+
resources.vertexUintView = new Uint32Array(resources.vertexData);
|
|
702
|
+
}
|
|
703
|
+
if (vertexBytes > resources.vertexBufferCapacity) {
|
|
704
|
+
resources.vertexBuffer?.destroy();
|
|
705
|
+
resources.vertexBufferCapacity = Math.max(vertexBytes, resources.vertexBufferCapacity * 2 || vertexStrideBytes);
|
|
706
|
+
resources.vertexBuffer = device.createBuffer({
|
|
707
|
+
size: resources.vertexBufferCapacity,
|
|
708
|
+
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
// Index buffer
|
|
712
|
+
const indexBytes = resources.totalIndices * Uint16Array.BYTES_PER_ELEMENT;
|
|
713
|
+
if (resources.indexData.length < resources.totalIndices) {
|
|
714
|
+
resources.indexData = new Uint16Array(Math.max(resources.totalIndices, resources.indexData.length * 2));
|
|
715
|
+
}
|
|
716
|
+
if (indexBytes > resources.indexBufferCapacity) {
|
|
717
|
+
resources.indexBuffer?.destroy();
|
|
718
|
+
resources.indexBufferCapacity = Math.max(indexBytes, resources.indexBufferCapacity * 2 || Uint16Array.BYTES_PER_ELEMENT);
|
|
719
|
+
resources.indexBuffer = device.createBuffer({
|
|
720
|
+
size: resources.indexBufferCapacity,
|
|
721
|
+
usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
// Mesh-uniform UBO (proj/trans/tint per draw, 256-byte aligned).
|
|
725
|
+
const meshUniformBytes = resources.drawCount * meshUniformAlignment;
|
|
726
|
+
if (meshUniformBytes > resources.meshUniformBufferCapacity) {
|
|
727
|
+
resources.meshUniformBuffer?.destroy();
|
|
728
|
+
resources.meshUniformBufferCapacity = Math.max(meshUniformBytes, resources.meshUniformBufferCapacity * 2 || meshUniformAlignment);
|
|
729
|
+
resources.meshUniformBuffer = device.createBuffer({
|
|
730
|
+
size: resources.meshUniformBufferCapacity,
|
|
731
|
+
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
732
|
+
});
|
|
733
|
+
resources.meshUniformBindGroup = device.createBindGroup({
|
|
734
|
+
layout: resources.meshUniformLayout,
|
|
735
|
+
entries: [
|
|
736
|
+
{
|
|
737
|
+
binding: 0,
|
|
738
|
+
resource: { buffer: resources.meshUniformBuffer, size: customMeshUniformBytes },
|
|
739
|
+
},
|
|
740
|
+
],
|
|
741
|
+
});
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
_writeMeshVerticesIntoBuffer(mesh, vertexStart, floatView, uintView) {
|
|
745
|
+
const vertices = mesh.vertices;
|
|
746
|
+
const uvs = mesh.uvs;
|
|
747
|
+
const colors = mesh.colors;
|
|
748
|
+
const vertexCount = mesh.vertexCount;
|
|
749
|
+
for (let i = 0; i < vertexCount; i++) {
|
|
750
|
+
const sourceIndex = i * 2;
|
|
751
|
+
const targetIndex = (vertexStart + i) * wordsPerVertex;
|
|
752
|
+
floatView[targetIndex + 0] = vertices[sourceIndex];
|
|
753
|
+
floatView[targetIndex + 1] = vertices[sourceIndex + 1];
|
|
754
|
+
floatView[targetIndex + 2] = uvs !== null ? uvs[sourceIndex] : 0;
|
|
755
|
+
floatView[targetIndex + 3] = uvs !== null ? uvs[sourceIndex + 1] : 0;
|
|
756
|
+
uintView[targetIndex + 4] = colors !== null ? colors[i] : 0xffffffff;
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
_writeCustomMeshUniform(_shader, resources, drawCursor, mesh, backend) {
|
|
760
|
+
// Layout: mat3x3 projection (48B) + mat3x3 translation (48B) + vec4 tint (16B) = 112B.
|
|
761
|
+
// WGSL mat3x3 stores 3 vec3 columns padded to vec4 alignment.
|
|
762
|
+
const slotBytes = meshUniformAlignment;
|
|
763
|
+
const slotFloats = slotBytes / Float32Array.BYTES_PER_ELEMENT;
|
|
764
|
+
const data = new Float32Array(slotFloats);
|
|
765
|
+
const proj = backend.view.getTransform();
|
|
766
|
+
const trans = mesh.getGlobalTransform();
|
|
767
|
+
// mat3 (column-major): [a, c, tx | b, d, ty | 0, 0, 1] in 2D.
|
|
768
|
+
// WGSL mat3x3 has each column padded to vec4. Store as:
|
|
769
|
+
// col0 = [a, b, 0, 0] / [c, d, 0, 0] / ...
|
|
770
|
+
// ExoJS Matrix stores: a, b, c, d, x, y. Standard 2D affine is:
|
|
771
|
+
// [a c tx]
|
|
772
|
+
// [b d ty]
|
|
773
|
+
// [0 0 1 ]
|
|
774
|
+
// Column-major mat3: col0 = (a, b, 0), col1 = (c, d, 0), col2 = (tx, ty, 1).
|
|
775
|
+
let off = 0;
|
|
776
|
+
// projection
|
|
777
|
+
data[off + 0] = proj.a;
|
|
778
|
+
data[off + 1] = proj.b;
|
|
779
|
+
data[off + 2] = 0;
|
|
780
|
+
data[off + 3] = 0; // pad
|
|
781
|
+
data[off + 4] = proj.c;
|
|
782
|
+
data[off + 5] = proj.d;
|
|
783
|
+
data[off + 6] = 0;
|
|
784
|
+
data[off + 7] = 0; // pad
|
|
785
|
+
data[off + 8] = proj.x;
|
|
786
|
+
data[off + 9] = proj.y;
|
|
787
|
+
data[off + 10] = 1;
|
|
788
|
+
data[off + 11] = 0; // pad
|
|
789
|
+
off += 12;
|
|
790
|
+
// translation
|
|
791
|
+
data[off + 0] = trans.a;
|
|
792
|
+
data[off + 1] = trans.b;
|
|
793
|
+
data[off + 2] = 0;
|
|
794
|
+
data[off + 3] = 0;
|
|
795
|
+
data[off + 4] = trans.c;
|
|
796
|
+
data[off + 5] = trans.d;
|
|
797
|
+
data[off + 6] = 0;
|
|
798
|
+
data[off + 7] = 0;
|
|
799
|
+
data[off + 8] = trans.x;
|
|
800
|
+
data[off + 9] = trans.y;
|
|
801
|
+
data[off + 10] = 1;
|
|
802
|
+
data[off + 11] = 0;
|
|
803
|
+
off += 12;
|
|
804
|
+
// tint (vec4)
|
|
805
|
+
const tint = mesh.tint;
|
|
806
|
+
data[off + 0] = tint.red;
|
|
807
|
+
data[off + 1] = tint.green;
|
|
808
|
+
data[off + 2] = tint.blue;
|
|
809
|
+
data[off + 3] = tint.alpha;
|
|
810
|
+
this._device.queue.writeBuffer(resources.meshUniformBuffer, drawCursor * slotBytes, data);
|
|
811
|
+
}
|
|
812
|
+
_getOrCreateCustomPipeline(resources, blendMode, format) {
|
|
813
|
+
const cacheKey = `${blendMode}:${format}`;
|
|
814
|
+
let pipeline = resources.pipelines.get(cacheKey);
|
|
815
|
+
if (pipeline === undefined) {
|
|
816
|
+
pipeline = this._device.createRenderPipeline({
|
|
817
|
+
layout: resources.pipelineLayout,
|
|
818
|
+
vertex: {
|
|
819
|
+
module: resources.shaderModule,
|
|
820
|
+
entryPoint: 'vertexMain',
|
|
821
|
+
buffers: [
|
|
822
|
+
{
|
|
823
|
+
arrayStride: vertexStrideBytes,
|
|
824
|
+
stepMode: 'vertex',
|
|
825
|
+
attributes: [
|
|
826
|
+
{ shaderLocation: 0, offset: 0, format: 'float32x2' },
|
|
827
|
+
{ shaderLocation: 1, offset: 8, format: 'float32x2' },
|
|
828
|
+
{ shaderLocation: 2, offset: 16, format: 'unorm8x4' },
|
|
829
|
+
],
|
|
830
|
+
},
|
|
831
|
+
],
|
|
832
|
+
},
|
|
833
|
+
fragment: {
|
|
834
|
+
module: resources.shaderModule,
|
|
835
|
+
entryPoint: 'fragmentMain',
|
|
836
|
+
targets: [
|
|
837
|
+
{
|
|
838
|
+
format,
|
|
839
|
+
blend: getWebGpuBlendState(blendMode),
|
|
840
|
+
writeMask: GPUColorWrite.ALL,
|
|
841
|
+
},
|
|
842
|
+
],
|
|
843
|
+
},
|
|
844
|
+
primitive: {
|
|
845
|
+
topology: 'triangle-list',
|
|
846
|
+
cullMode: 'none',
|
|
847
|
+
},
|
|
848
|
+
});
|
|
849
|
+
resources.pipelines.set(cacheKey, pipeline);
|
|
850
|
+
}
|
|
851
|
+
return pipeline;
|
|
852
|
+
}
|
|
853
|
+
_getOrCreateCustomMeshTextureBindGroup(resources, backend, texture) {
|
|
854
|
+
let group = resources.meshTextureBindGroups.get(texture);
|
|
855
|
+
if (group === undefined) {
|
|
856
|
+
const binding = backend.getTextureBinding(texture);
|
|
857
|
+
group = this._device.createBindGroup({
|
|
858
|
+
layout: resources.meshTextureLayout,
|
|
859
|
+
entries: [
|
|
860
|
+
{ binding: 0, resource: binding.view },
|
|
861
|
+
{ binding: 1, resource: binding.sampler },
|
|
862
|
+
],
|
|
863
|
+
});
|
|
864
|
+
resources.meshTextureBindGroups.set(texture, group);
|
|
865
|
+
}
|
|
866
|
+
return group;
|
|
867
|
+
}
|
|
868
|
+
_buildUserBindGroupLayout(device, shader) {
|
|
869
|
+
const entries = [];
|
|
870
|
+
const userUniforms = shader.uniforms;
|
|
871
|
+
Object.values(userUniforms).some(v => !isTextureUniform(v));
|
|
872
|
+
// Binding 0 always reserved for the user UBO (even if empty), so the
|
|
873
|
+
// bind-group layout is stable across user-uniform mutations.
|
|
874
|
+
entries.push({
|
|
875
|
+
binding: 0,
|
|
876
|
+
visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
|
|
877
|
+
buffer: { type: 'uniform' },
|
|
878
|
+
});
|
|
879
|
+
let bindingIndex = 1;
|
|
880
|
+
let textureCount = 0;
|
|
881
|
+
for (const value of Object.values(userUniforms)) {
|
|
882
|
+
if (!isTextureUniform(value)) {
|
|
883
|
+
continue;
|
|
884
|
+
}
|
|
885
|
+
if (textureCount >= maxCustomTextureSlots) {
|
|
886
|
+
throw new Error(`MeshShader requested more than ${maxCustomTextureSlots} user texture uniforms.`);
|
|
887
|
+
}
|
|
888
|
+
entries.push({
|
|
889
|
+
binding: bindingIndex,
|
|
890
|
+
visibility: GPUShaderStage.FRAGMENT,
|
|
891
|
+
texture: { sampleType: 'float' },
|
|
892
|
+
});
|
|
893
|
+
bindingIndex++;
|
|
894
|
+
entries.push({
|
|
895
|
+
binding: bindingIndex,
|
|
896
|
+
visibility: GPUShaderStage.FRAGMENT,
|
|
897
|
+
sampler: { type: 'filtering' },
|
|
898
|
+
});
|
|
899
|
+
bindingIndex++;
|
|
900
|
+
textureCount++;
|
|
901
|
+
}
|
|
902
|
+
return device.createBindGroupLayout({ entries });
|
|
903
|
+
}
|
|
904
|
+
_uploadUserUniforms(_shader, resources) {
|
|
905
|
+
const device = this._device;
|
|
906
|
+
const uniforms = _shader.uniforms;
|
|
907
|
+
const scalarValues = Object.values(uniforms).filter(v => !isTextureUniform(v));
|
|
908
|
+
// Always create a UBO (even if empty) since binding 0 of the user layout
|
|
909
|
+
// is fixed. Min size 16 bytes to satisfy WebGPU's minimum buffer size.
|
|
910
|
+
const slotCount = Math.max(scalarValues.length, 1);
|
|
911
|
+
const bufferBytes = slotCount * 16;
|
|
912
|
+
if (resources.userUniformBuffer === null || resources.userUniformBufferCapacity < bufferBytes) {
|
|
913
|
+
resources.userUniformBuffer?.destroy();
|
|
914
|
+
resources.userUniformBufferCapacity = bufferBytes;
|
|
915
|
+
resources.userUniformBuffer = device.createBuffer({
|
|
916
|
+
size: bufferBytes,
|
|
917
|
+
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
433
918
|
});
|
|
434
919
|
}
|
|
920
|
+
const data = new Float32Array(bufferBytes / 4);
|
|
921
|
+
let slot = 0;
|
|
922
|
+
for (const value of scalarValues) {
|
|
923
|
+
const baseFloatIndex = slot * 4;
|
|
924
|
+
if (typeof value === 'number') {
|
|
925
|
+
data[baseFloatIndex] = value;
|
|
926
|
+
}
|
|
927
|
+
else if (value instanceof Float32Array) {
|
|
928
|
+
data.set(value, baseFloatIndex);
|
|
929
|
+
}
|
|
930
|
+
else if (value instanceof Int32Array) {
|
|
931
|
+
for (let i = 0; i < value.length; i++) {
|
|
932
|
+
data[baseFloatIndex + i] = value[i];
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
else {
|
|
936
|
+
const arr = value;
|
|
937
|
+
for (let i = 0; i < arr.length; i++) {
|
|
938
|
+
data[baseFloatIndex + i] = arr[i];
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
slot++;
|
|
942
|
+
}
|
|
943
|
+
device.queue.writeBuffer(resources.userUniformBuffer, 0, data);
|
|
435
944
|
}
|
|
945
|
+
_buildUserBindGroup(backend, shader, resources) {
|
|
946
|
+
const device = this._device;
|
|
947
|
+
const entries = [];
|
|
948
|
+
entries.push({ binding: 0, resource: { buffer: resources.userUniformBuffer } });
|
|
949
|
+
let bindingIndex = 1;
|
|
950
|
+
for (const value of Object.values(shader.uniforms)) {
|
|
951
|
+
if (!isTextureUniform(value)) {
|
|
952
|
+
continue;
|
|
953
|
+
}
|
|
954
|
+
const binding = backend.getTextureBinding(value);
|
|
955
|
+
entries.push({ binding: bindingIndex, resource: binding.view });
|
|
956
|
+
bindingIndex++;
|
|
957
|
+
entries.push({ binding: bindingIndex, resource: binding.sampler });
|
|
958
|
+
bindingIndex++;
|
|
959
|
+
}
|
|
960
|
+
return device.createBindGroup({
|
|
961
|
+
layout: resources.userLayout,
|
|
962
|
+
entries,
|
|
963
|
+
});
|
|
964
|
+
}
|
|
965
|
+
_releaseCustomShaderResources(resources) {
|
|
966
|
+
resources.vertexBuffer?.destroy();
|
|
967
|
+
resources.indexBuffer?.destroy();
|
|
968
|
+
resources.meshUniformBuffer?.destroy();
|
|
969
|
+
resources.userUniformBuffer?.destroy();
|
|
970
|
+
resources.pipelines.clear();
|
|
971
|
+
resources.meshTextureBindGroups.clear();
|
|
972
|
+
resources.vertexBuffer = null;
|
|
973
|
+
resources.indexBuffer = null;
|
|
974
|
+
resources.meshUniformBuffer = null;
|
|
975
|
+
resources.userUniformBuffer = null;
|
|
976
|
+
resources.meshUniformBindGroup = null;
|
|
977
|
+
resources.vertexBufferCapacity = 0;
|
|
978
|
+
resources.indexBufferCapacity = 0;
|
|
979
|
+
resources.meshUniformBufferCapacity = 0;
|
|
980
|
+
resources.userUniformBufferCapacity = 0;
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
function isTextureUniform(value) {
|
|
984
|
+
return (typeof value === 'object' &&
|
|
985
|
+
value !== null &&
|
|
986
|
+
'width' in value &&
|
|
987
|
+
'height' in value &&
|
|
988
|
+
!(value instanceof Float32Array) &&
|
|
989
|
+
!(value instanceof Int32Array) &&
|
|
990
|
+
!Array.isArray(value));
|
|
436
991
|
}
|
|
437
992
|
|
|
438
993
|
export { WebGpuMeshRenderer };
|