@haiyue/extensions 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (269) hide show
  1. package/README.md +25 -0
  2. package/dist/animation/Animation2DComponent.d.ts +18 -0
  3. package/dist/animation/Animation2DExtensionRegistry.d.ts +7 -0
  4. package/dist/animation/Animation2DRenderSystem.d.ts +6 -0
  5. package/dist/animation/Animation2DRuntime.d.ts +6 -2
  6. package/dist/animation/AnimationTextRasterizer.d.ts +4 -0
  7. package/dist/animation/AnimationVisual2D.d.ts +58 -2
  8. package/dist/animation/interaction/InteractionRuntime.d.ts +83 -0
  9. package/dist/animation/interaction/runtime-types.d.ts +257 -0
  10. package/dist/animation.d.ts +3 -1
  11. package/dist/animation.js +551 -59
  12. package/dist/animation3d.js +1 -1
  13. package/dist/canvas-text.js +1 -1
  14. package/dist/chunks/2d-ui-animation-2d.generated-FpziAeGJ.js +4 -0
  15. package/dist/chunks/2d-ui-indexed-sprite.generated-DmKR0CC3.js +4 -0
  16. package/dist/chunks/Animation2DStateMachine-BHcA6Cu3.js +18212 -0
  17. package/dist/chunks/{AnimationStateMachineController-BIcAh-sT.js → AnimationStateMachineController-4sKaqh9E.js} +2 -2
  18. package/dist/chunks/AnimationVisual2D-8y3qp0_U.js +230 -0
  19. package/dist/chunks/{RenderSystem2DBase-DDT61dAu.js → RenderSystem2DBase-CF2s00vg.js} +2 -2
  20. package/dist/controls/VirtualJoystickControls.d.ts +46 -0
  21. package/dist/controls/contracts.d.ts +91 -0
  22. package/dist/controls.d.ts +2 -0
  23. package/dist/controls.js +358 -0
  24. package/dist/deformable-animation/runtime/DeformableMesh2DRuntimeExtension.d.ts +11 -0
  25. package/dist/deformable-animation/runtime/DeformableMesh2DSampler.d.ts +11 -0
  26. package/dist/deformable-animation.d.ts +5 -0
  27. package/dist/deformable-animation.js +316 -0
  28. package/dist/experimental-indexed-sprite.d.ts +5 -0
  29. package/dist/experimental-indexed-sprite.js +482 -0
  30. package/dist/gltf-worker-runtime.js +9 -9
  31. package/dist/hya-state-machine.js +3 -2
  32. package/dist/index.js +1 -1
  33. package/dist/indexed-sprite/AtlasLayout.d.ts +2 -0
  34. package/dist/indexed-sprite/IndexedSpriteRenderer.d.ts +23 -0
  35. package/dist/indexed-sprite/contracts.d.ts +89 -0
  36. package/dist/internal/2d-ui-shader-artifact.js +232 -23
  37. package/dist/ray-tracing/acceleration/bvh.d.ts +37 -0
  38. package/dist/ray-tracing/acceleration/index.d.ts +6 -0
  39. package/dist/ray-tracing/acceleration/pack.d.ts +152 -0
  40. package/dist/ray-tracing/acceleration/query.d.ts +4 -0
  41. package/dist/ray-tracing/acceleration/runtime.d.ts +30 -0
  42. package/dist/ray-tracing/acceleration/tlas.d.ts +4 -0
  43. package/dist/ray-tracing/acceleration/types.d.ts +165 -0
  44. package/dist/ray-tracing/denoise/index.d.ts +3 -0
  45. package/dist/ray-tracing/denoise/layout.d.ts +9 -0
  46. package/dist/ray-tracing/denoise/runtime.d.ts +23 -0
  47. package/dist/ray-tracing/denoise/types.d.ts +28 -0
  48. package/dist/ray-tracing/hybrid/contract.d.ts +2 -0
  49. package/dist/ray-tracing/hybrid/index.d.ts +5 -0
  50. package/dist/ray-tracing/hybrid/layout.d.ts +17 -0
  51. package/dist/ray-tracing/hybrid/plan.d.ts +2 -0
  52. package/dist/ray-tracing/hybrid/runtime.d.ts +31 -0
  53. package/dist/ray-tracing/hybrid/shaders/hybrid-artifact.generated.d.ts +625 -0
  54. package/dist/ray-tracing/hybrid/types.d.ts +144 -0
  55. package/dist/ray-tracing/lifecycle/device-recovery.d.ts +31 -0
  56. package/dist/ray-tracing/lifecycle/index.d.ts +2 -0
  57. package/dist/ray-tracing/lifecycle/types.d.ts +37 -0
  58. package/dist/ray-tracing/material/index.d.ts +2 -0
  59. package/dist/ray-tracing/material/pack.d.ts +5 -0
  60. package/dist/ray-tracing/material/types.d.ts +63 -0
  61. package/dist/ray-tracing/reference/index.d.ts +122 -0
  62. package/dist/ray-tracing/renderer/index.d.ts +7 -0
  63. package/dist/ray-tracing/renderer/layout.d.ts +20 -0
  64. package/dist/ray-tracing/renderer/plan.d.ts +2 -0
  65. package/dist/ray-tracing/renderer/reference.d.ts +17 -0
  66. package/dist/ray-tracing/renderer/runtime.d.ts +39 -0
  67. package/dist/ray-tracing/renderer/scene.d.ts +4 -0
  68. package/dist/ray-tracing/renderer/types.d.ts +115 -0
  69. package/dist/ray-tracing/sampling/index.d.ts +8 -0
  70. package/dist/ray-tracing/sampling/layout.d.ts +12 -0
  71. package/dist/ray-tracing/sampling/plan.d.ts +5 -0
  72. package/dist/ray-tracing/sampling/revision.d.ts +15 -0
  73. package/dist/ray-tracing/sampling/runtime.d.ts +44 -0
  74. package/dist/ray-tracing/sampling/sequence.d.ts +2 -0
  75. package/dist/ray-tracing/sampling/types.d.ts +96 -0
  76. package/dist/ray-tracing/scene/index.d.ts +41 -0
  77. package/dist/ray-tracing/traversal/index.d.ts +4 -0
  78. package/dist/ray-tracing/traversal/layout.d.ts +16 -0
  79. package/dist/ray-tracing/traversal/plan.d.ts +2 -0
  80. package/dist/ray-tracing/traversal/runtime.d.ts +25 -0
  81. package/dist/ray-tracing/traversal/types.d.ts +79 -0
  82. package/dist/ray-tracing/worker/client.d.ts +25 -0
  83. package/dist/ray-tracing/worker/index.d.ts +5 -0
  84. package/dist/ray-tracing/worker/runtime.d.ts +11 -0
  85. package/dist/ray-tracing/worker/serialization.d.ts +16 -0
  86. package/dist/ray-tracing/worker/source.d.ts +1 -0
  87. package/dist/ray-tracing/worker/types.d.ts +91 -0
  88. package/dist/ray-tracing.d.ts +17 -0
  89. package/dist/ray-tracing.js +7666 -0
  90. package/dist/spine.js +1 -1
  91. package/dist/tilemap.js +1 -1
  92. package/package.json +26 -23
  93. package/dist/animation/Animation2DStateMachineVisualRuntime.d.ts +0 -27
  94. package/dist/animation/Particle2DDescriptorAdapter.d.ts +0 -5
  95. package/dist/animation3d/Animation3DResource.d.ts +0 -28
  96. package/dist/animation3d/hya/HyaAnimation3DStateMachineRuntime.d.ts +0 -65
  97. package/dist/animation3d/hya/index.d.ts +0 -4
  98. package/dist/animation3d/runtime/integration/Animation3DStateMachineMixerAdapter.d.ts +0 -81
  99. package/dist/animation3d/runtime/integration/Animation3DStateMachineMixerIntegration.d.ts +0 -39
  100. package/dist/animation3d/runtime/integration/index.d.ts +0 -2
  101. package/dist/animation3d/runtime/mixer/Action.d.ts +0 -150
  102. package/dist/animation3d/runtime/mixer/Animation3DMixerRuntimeStore.d.ts +0 -6
  103. package/dist/animation3d/runtime/mixer/Mixer.d.ts +0 -59
  104. package/dist/animation3d/runtime/mixer/PoseBuffer.d.ts +0 -1
  105. package/dist/animation3d/runtime/mixer/TrackSampler.d.ts +0 -24
  106. package/dist/animation3d/runtime/mixer/index.d.ts +0 -4
  107. package/dist/animation3d/runtime/state-machine/Animation3DBlendTreeWeights.d.ts +0 -1
  108. package/dist/animation3d/runtime/state-machine/Animation3DStateMachineCompiler.d.ts +0 -1
  109. package/dist/animation3d/runtime/state-machine/Animation3DStateMachineController.d.ts +0 -1
  110. package/dist/animation3d/runtime/state-machine/Animation3DStateMachineMixerPort.d.ts +0 -1
  111. package/dist/animation3d/runtime/state-machine/index.d.ts +0 -6
  112. package/dist/benchmark.d.ts +0 -30
  113. package/dist/chunks/2d-ui-animation-2d.generated-D4aHzFU2.js +0 -4
  114. package/dist/chunks/Animation2DStateMachine-9IonguNa.js +0 -3245
  115. package/dist/gltf/GltfAccessorReader.d.ts +0 -7
  116. package/dist/gltf/GltfDracoDecoder.d.ts +0 -4
  117. package/dist/gltf/GltfLoaderErrors.d.ts +0 -4
  118. package/dist/gltf/GltfMaterialDescriptor.d.ts +0 -23
  119. package/dist/gltf/GltfMaterialLoader.d.ts +0 -6
  120. package/dist/gltf-worker-runtime.d.ts +0 -7
  121. package/dist/plugins/pluginUtils.d.ts +0 -20
  122. package/dist/plugins.d.ts +0 -6
  123. package/dist/shaders/generated/2d-ui-artifact.generated.d.ts +0 -658
  124. package/dist/spine/Spine2DGpuRenderer.d.ts +0 -65
  125. package/dist/spine/Spine2DRuntime.d.ts +0 -103
  126. package/dist/spine/SpineAnimationBoneTimelines.d.ts +0 -8
  127. package/dist/spine/SpineFloatBuilder.d.ts +0 -15
  128. package/dist/spine/SpinePathConstraintSolver.d.ts +0 -32
  129. package/dist/spine/SpineSkeletonRuntime.d.ts +0 -185
  130. package/dist/spine/SpineSlotAnimation.d.ts +0 -17
  131. package/dist/spine/SpineTimelineRuntime.d.ts +0 -25
  132. package/dist/spine/SpineVertexBuilder.d.ts +0 -61
  133. package/dist/tilemap/Tilemap2DRenderer.d.ts +0 -24
  134. package/dist/utils/arrayAccess.d.ts +0 -4
  135. package/dist/utils/camera2d.d.ts +0 -12
  136. package/src/animation/Animation2DComponent.ts +0 -135
  137. package/src/animation/Animation2DExtensionRegistry.ts +0 -52
  138. package/src/animation/Animation2DRenderSystem.ts +0 -1026
  139. package/src/animation/Animation2DRuntime.ts +0 -1326
  140. package/src/animation/Animation2DStateMachine.ts +0 -39
  141. package/src/animation/Animation2DStateMachineComponent.ts +0 -228
  142. package/src/animation/Animation2DStateMachineRuntime.ts +0 -141
  143. package/src/animation/Animation2DStateMachineSystem.ts +0 -60
  144. package/src/animation/Animation2DStateMachineVisualRuntime.ts +0 -282
  145. package/src/animation/Animation2DSystem.ts +0 -72
  146. package/src/animation/AnimationAssetLoader.ts +0 -84
  147. package/src/animation/AnimationAudioClip.ts +0 -140
  148. package/src/animation/AnimationPathTessellator.ts +0 -691
  149. package/src/animation/AnimationTextRasterizer.ts +0 -340
  150. package/src/animation/AnimationVisual2D.ts +0 -153
  151. package/src/animation/HyaAnimation2DClipAdapter.ts +0 -845
  152. package/src/animation/Particle2DDescriptorAdapter.ts +0 -35
  153. package/src/animation/runtime/mixer/Action.ts +0 -876
  154. package/src/animation/runtime/mixer/Mixer.ts +0 -505
  155. package/src/animation/runtime/mixer/PoseBuffer.ts +0 -105
  156. package/src/animation/runtime/mixer/Sampler.ts +0 -252
  157. package/src/animation/runtime/mixer/Types.ts +0 -138
  158. package/src/animation/runtime/mixer/index.ts +0 -31
  159. package/src/animation-state-machine/AnimationStateMachine.ts +0 -146
  160. package/src/animation-state-machine/AnimationStateMachineChannels.ts +0 -116
  161. package/src/animation-state-machine/runtime/Animation2DStateMachineMixerAdapter.ts +0 -197
  162. package/src/animation-state-machine/runtime/AnimationStateMachineBlendTreeWeights.ts +0 -503
  163. package/src/animation-state-machine/runtime/AnimationStateMachineCompiler.ts +0 -763
  164. package/src/animation-state-machine/runtime/AnimationStateMachineController.ts +0 -987
  165. package/src/animation-state-machine/runtime/AnimationStateMachineMixerPort.ts +0 -54
  166. package/src/animation-state-machine/runtime/index.ts +0 -47
  167. package/src/animation.ts +0 -33
  168. package/src/animation3d/Animation3DAction.ts +0 -73
  169. package/src/animation3d/Animation3DBinding.ts +0 -116
  170. package/src/animation3d/Animation3DClip.ts +0 -22
  171. package/src/animation3d/Animation3DError.ts +0 -34
  172. package/src/animation3d/Animation3DMixer.ts +0 -82
  173. package/src/animation3d/Animation3DPose.ts +0 -100
  174. package/src/animation3d/Animation3DPoseBuffer.ts +0 -85
  175. package/src/animation3d/Animation3DResource.ts +0 -49
  176. package/src/animation3d/Animation3DStateMachine.ts +0 -126
  177. package/src/animation3d/Animation3DStateMachineRuntime.ts +0 -191
  178. package/src/animation3d/Animation3DTrack.ts +0 -29
  179. package/src/animation3d/hya/HyaAnimation3DRuntime.ts +0 -756
  180. package/src/animation3d/hya/HyaAnimation3DStateMachineRuntime.ts +0 -618
  181. package/src/animation3d/hya/HyaAnimation3DTypes.ts +0 -168
  182. package/src/animation3d/hya/index.ts +0 -10
  183. package/src/animation3d/index.ts +0 -76
  184. package/src/animation3d/runtime/integration/Animation3DStateMachineMixerAdapter.ts +0 -325
  185. package/src/animation3d/runtime/integration/Animation3DStateMachineMixerIntegration.ts +0 -158
  186. package/src/animation3d/runtime/integration/index.ts +0 -9
  187. package/src/animation3d/runtime/mixer/Action.ts +0 -833
  188. package/src/animation3d/runtime/mixer/Animation3DMixerRuntimeStore.ts +0 -26
  189. package/src/animation3d/runtime/mixer/Mixer.ts +0 -562
  190. package/src/animation3d/runtime/mixer/PoseBuffer.ts +0 -1
  191. package/src/animation3d/runtime/mixer/TrackSampler.ts +0 -399
  192. package/src/animation3d/runtime/mixer/index.ts +0 -8
  193. package/src/animation3d/runtime/state-machine/Animation3DBlendTreeWeights.ts +0 -4
  194. package/src/animation3d/runtime/state-machine/Animation3DStateMachineCompiler.ts +0 -1
  195. package/src/animation3d/runtime/state-machine/Animation3DStateMachineController.ts +0 -1
  196. package/src/animation3d/runtime/state-machine/Animation3DStateMachineMixerPort.ts +0 -1
  197. package/src/animation3d/runtime/state-machine/index.ts +0 -45
  198. package/src/animation3d.ts +0 -5
  199. package/src/benchmark.ts +0 -168
  200. package/src/canvas-text/CanvasText2DRenderSystem.ts +0 -292
  201. package/src/canvas-text/CanvasTextComponent.ts +0 -47
  202. package/src/canvas-text.ts +0 -4
  203. package/src/experimental-gltf-worker.ts +0 -18
  204. package/src/experimental-spine-worker.ts +0 -9
  205. package/src/gltf/GltfAccessorReader.ts +0 -271
  206. package/src/gltf/GltfAnimation3DAdapter.ts +0 -680
  207. package/src/gltf/GltfAnimationRuntime.ts +0 -392
  208. package/src/gltf/GltfAssetWorkerClient.ts +0 -136
  209. package/src/gltf/GltfCompatibilityReport.ts +0 -214
  210. package/src/gltf/GltfConservativeBounds.ts +0 -219
  211. package/src/gltf/GltfDracoDecoder.ts +0 -251
  212. package/src/gltf/GltfExtensionAdapter.ts +0 -340
  213. package/src/gltf/GltfLoaderContract.ts +0 -333
  214. package/src/gltf/GltfLoaderErrors.ts +0 -59
  215. package/src/gltf/GltfMaterialDescriptor.ts +0 -134
  216. package/src/gltf/GltfMaterialEncoder.ts +0 -173
  217. package/src/gltf/GltfMaterialLoader.ts +0 -293
  218. package/src/gltf/GltfModelComponent.ts +0 -115
  219. package/src/gltf/GltfModelSystem.ts +0 -208
  220. package/src/gltf/GltfPlugin.ts +0 -84
  221. package/src/gltf/GltfSchema.ts +0 -229
  222. package/src/gltf/GltfUvSemanticPlanner.ts +0 -166
  223. package/src/gltf/gltfLoader.ts +0 -910
  224. package/src/gltf-animation3d.ts +0 -10
  225. package/src/gltf-worker-runtime.ts +0 -10
  226. package/src/gltf.ts +0 -56
  227. package/src/grid/Grid2DComponent.ts +0 -69
  228. package/src/grid.ts +0 -2
  229. package/src/hya-state-machine.ts +0 -19
  230. package/src/index.ts +0 -2
  231. package/src/plugins/pluginUtils.ts +0 -50
  232. package/src/plugins.ts +0 -6
  233. package/src/shaders/generated/2d-ui-animation-2d.generated.wgsl +0 -242
  234. package/src/shaders/generated/2d-ui-artifact.generated.ts +0 -867
  235. package/src/shaders/generated/2d-ui-canvas-text-2d.generated.wgsl +0 -38
  236. package/src/shaders/generated/2d-ui-spine2d.generated.wgsl +0 -41
  237. package/src/shaders/generated/2d-ui-tilemap2d.generated.wgsl +0 -36
  238. package/src/spine/Spine2DComponent.ts +0 -168
  239. package/src/spine/Spine2DGpuRenderer.ts +0 -449
  240. package/src/spine/Spine2DRenderSystem.ts +0 -199
  241. package/src/spine/Spine2DRuntime.ts +0 -1529
  242. package/src/spine/SpineAnimationBoneTimelines.ts +0 -30
  243. package/src/spine/SpineAssetParser.ts +0 -20
  244. package/src/spine/SpineAssetWorkerClient.ts +0 -112
  245. package/src/spine/SpineAssetWorkerContract.ts +0 -4
  246. package/src/spine/SpineAtlasParser.ts +0 -127
  247. package/src/spine/SpineFloatBuilder.ts +0 -66
  248. package/src/spine/SpinePathConstraintSolver.ts +0 -582
  249. package/src/spine/SpinePlugin.ts +0 -76
  250. package/src/spine/SpineSkeletonRuntime.ts +0 -521
  251. package/src/spine/SpineSlotAnimation.ts +0 -195
  252. package/src/spine/SpineTimelineRuntime.ts +0 -453
  253. package/src/spine/SpineVertexBuilder.ts +0 -686
  254. package/src/spine.ts +0 -7
  255. package/src/tilemap/Tilemap2DComponent.ts +0 -232
  256. package/src/tilemap/Tilemap2DRenderSystem.ts +0 -58
  257. package/src/tilemap/Tilemap2DRenderer.ts +0 -289
  258. package/src/tilemap/TilemapPlugin.ts +0 -75
  259. package/src/tilemap.ts +0 -6
  260. package/src/tween/Tween2DComponent.ts +0 -83
  261. package/src/tween/Tween2DSystem.ts +0 -78
  262. package/src/tween.ts +0 -4
  263. package/src/types/earcut.d.ts +0 -7
  264. package/src/types/wgsl.d.ts +0 -4
  265. package/src/utils/RenderSystem2DBase.ts +0 -164
  266. package/src/utils/arrayAccess.ts +0 -17
  267. package/src/utils/camera2d.ts +0 -68
  268. package/src/utils/render2dGpu.ts +0 -132
  269. package/test/fixtures/gltf/animation-characterization.gltf +0 -344
@@ -0,0 +1,482 @@
1
+ import { I as IndexedSpriteWgsl } from './chunks/2d-ui-indexed-sprite.generated-DmKR0CC3.js';
2
+
3
+ const DEFAULT_INDEXED_SPRITE_ATLAS_LIMITS = Object.freeze({
4
+ maxTextureDimension2D: 8_192,
5
+ maxAtlasPages: 64,
6
+ maxGpuBytes: 536_870_912,
7
+ maxPaletteGpuBytes: 16_777_216,
8
+ maxUploadBytesPerFrame: 16_777_216,
9
+ maxDrawCommandsPerFrame: 16_384,
10
+ });
11
+
12
+ function prepareIndexedSpriteAtlas(sprites, palettes, limits = DEFAULT_INDEXED_SPRITE_ATLAS_LIMITS) {
13
+ validateLimits(limits);
14
+ const sortedSprites = [...sprites].sort((left, right) => compare(left.id, right.id));
15
+ const sortedPalettes = [...palettes].sort((left, right) => compare(left.id, right.id));
16
+ assertUnique(sortedSprites.map(value => value.id), 'sprite');
17
+ assertUnique(sortedPalettes.map(value => value.id), 'palette');
18
+ const mutablePages = [];
19
+ const mutablePlacements = new Map();
20
+ for (const sprite of sortedSprites) {
21
+ validateSprite(sprite, limits);
22
+ const kind = sprite.format === 'indexed8' ? 'indexed' : 'color';
23
+ const sourceBytesPerPixel = sprite.format === 'indexed8' ? 1 : sprite.format === 'rgb8' ? 3 : 4;
24
+ let page = mutablePages.find(value => value.kind === kind && canPlace(value, sprite.width, sprite.height, limits.maxTextureDimension2D));
25
+ if (!page) {
26
+ if (mutablePages.length >= limits.maxAtlasPages)
27
+ throw new RangeError(`Indexed sprite atlas exceeds maxAtlasPages=${limits.maxAtlasPages}.`);
28
+ page = { kind, placements: [], x: 0, y: 0, shelfHeight: 0, usedWidth: 0, usedHeight: 0 };
29
+ mutablePages.push(page);
30
+ }
31
+ const point = place(page, sprite.width, sprite.height, limits.maxTextureDimension2D);
32
+ const placement = Object.freeze({ spriteId: sprite.id, pageIndex: mutablePages.indexOf(page), pageKind: kind, x: point.x, y: point.y, width: sprite.width, height: sprite.height });
33
+ page.placements.push({ ...placement, source: sprite.pixels.slice(), sourceBytesPerPixel });
34
+ mutablePlacements.set(sprite.id, placement);
35
+ }
36
+ const pages = mutablePages.map((page, index) => finalizePage(page, index));
37
+ const mutablePaletteRows = new Map();
38
+ const paletteHeight = Math.max(1, sortedPalettes.length);
39
+ const palettePixels = new Uint8Array(256 * paletteHeight * 4);
40
+ sortedPalettes.forEach((palette, row) => {
41
+ validatePalette(palette);
42
+ mutablePaletteRows.set(palette.id, row);
43
+ palettePixels.set(palette.rgba, row * 256 * 4);
44
+ });
45
+ if (palettePixels.byteLength > limits.maxPaletteGpuBytes)
46
+ throw new RangeError(`Indexed sprite palette bank exceeds maxPaletteGpuBytes=${limits.maxPaletteGpuBytes}.`);
47
+ const pageBytes = pages.reduce((total, page) => total + page.pixels.byteLength, 0);
48
+ const gpuBytes = pageBytes + palettePixels.byteLength;
49
+ if (gpuBytes > limits.maxGpuBytes)
50
+ throw new RangeError(`Indexed sprite atlas exceeds maxGpuBytes=${limits.maxGpuBytes}.`);
51
+ return Object.freeze({
52
+ pages: Object.freeze(pages),
53
+ placements: readonlyMap(mutablePlacements),
54
+ paletteRows: readonlyMap(mutablePaletteRows),
55
+ paletteWidth: 256, paletteHeight, palettePixels,
56
+ cpuBytes: gpuBytes, gpuBytes,
57
+ });
58
+ }
59
+ function readonlyMap(source) {
60
+ const view = {
61
+ get size() { return source.size; },
62
+ has: key => source.has(key),
63
+ get: key => source.get(key),
64
+ entries: () => source.entries(),
65
+ keys: () => source.keys(),
66
+ values: () => source.values(),
67
+ forEach: (callback, thisArg) => source.forEach((value, key) => callback.call(thisArg, value, key, view)),
68
+ [Symbol.iterator]: () => source[Symbol.iterator](),
69
+ };
70
+ return Object.freeze(view);
71
+ }
72
+ function validateSprite(sprite, limits) {
73
+ if (!sprite.id || !Number.isSafeInteger(sprite.width) || !Number.isSafeInteger(sprite.height) || sprite.width < 1 || sprite.height < 1 || sprite.width > limits.maxTextureDimension2D - 2 || sprite.height > limits.maxTextureDimension2D - 2)
74
+ throw new RangeError(`Sprite ${sprite.id || '<empty>'} dimensions are invalid or exceed the atlas limit.`);
75
+ const bytesPerPixel = sprite.format === 'indexed8' ? 1 : sprite.format === 'rgb8' ? 3 : sprite.format === 'rgba8' ? 4 : 0;
76
+ if (bytesPerPixel === 0 || sprite.pixels.byteLength !== sprite.width * sprite.height * bytesPerPixel)
77
+ throw new RangeError(`Sprite ${sprite.id} pixel byte length does not match its descriptor.`);
78
+ }
79
+ function validatePalette(palette) {
80
+ if (!palette.id || !Number.isSafeInteger(palette.colorCount) || palette.colorCount < 1 || palette.colorCount > 256 || palette.rgba.byteLength !== palette.colorCount * 4)
81
+ throw new RangeError(`Palette ${palette.id || '<empty>'} is invalid.`);
82
+ }
83
+ function validateLimits(limits) { for (const [key, value] of Object.entries(limits))
84
+ if (!Number.isSafeInteger(value) || value < 1)
85
+ throw new RangeError(`Indexed sprite limit ${key} must be a positive safe integer.`); }
86
+ function assertUnique(values, label) { const seen = new Set(); for (const value of values) {
87
+ if (seen.has(value))
88
+ throw new RangeError(`Duplicate indexed sprite ${label} id: ${value}.`);
89
+ seen.add(value);
90
+ } }
91
+ function compare(left, right) { const count = Math.min(left.length, right.length); for (let i = 0; i < count; i++) {
92
+ const difference = left.charCodeAt(i) - right.charCodeAt(i);
93
+ if (difference !== 0)
94
+ return difference;
95
+ } return left.length - right.length; }
96
+ function canPlace(page, width, height, maximum) {
97
+ const paddedWidth = width + 2;
98
+ const paddedHeight = height + 2;
99
+ if (page.x + paddedWidth <= maximum && page.y + paddedHeight <= maximum)
100
+ return true;
101
+ return paddedWidth <= maximum && page.y + page.shelfHeight + paddedHeight <= maximum;
102
+ }
103
+ function place(page, width, height, maximum) {
104
+ const paddedWidth = width + 2;
105
+ const paddedHeight = height + 2;
106
+ if (page.x + paddedWidth > maximum) {
107
+ page.x = 0;
108
+ page.y += page.shelfHeight;
109
+ page.shelfHeight = 0;
110
+ }
111
+ const point = { x: page.x + 1, y: page.y + 1 };
112
+ page.x += paddedWidth;
113
+ page.shelfHeight = Math.max(page.shelfHeight, paddedHeight);
114
+ page.usedWidth = Math.max(page.usedWidth, page.x);
115
+ page.usedHeight = Math.max(page.usedHeight, page.y + page.shelfHeight);
116
+ return point;
117
+ }
118
+ function finalizePage(page, index) {
119
+ const bytesPerPixel = page.kind === 'indexed' ? 1 : 4;
120
+ const pixels = new Uint8Array(page.usedWidth * page.usedHeight * bytesPerPixel);
121
+ for (const placement of page.placements) {
122
+ for (let y = -1; y <= placement.height; y++) {
123
+ const sourceY = Math.max(0, Math.min(placement.height - 1, y));
124
+ for (let x = -1; x <= placement.width; x++) {
125
+ const sourceX = Math.max(0, Math.min(placement.width - 1, x));
126
+ const sourceOffset = (sourceY * placement.width + sourceX) * placement.sourceBytesPerPixel;
127
+ const targetOffset = ((placement.y + y) * page.usedWidth + placement.x + x) * bytesPerPixel;
128
+ if (bytesPerPixel === 1)
129
+ pixels[targetOffset] = placement.source[sourceOffset];
130
+ else {
131
+ pixels[targetOffset] = placement.source[sourceOffset];
132
+ pixels[targetOffset + 1] = placement.source[sourceOffset + 1];
133
+ pixels[targetOffset + 2] = placement.source[sourceOffset + 2];
134
+ pixels[targetOffset + 3] = placement.sourceBytesPerPixel === 4 ? placement.source[sourceOffset + 3] : 255;
135
+ }
136
+ }
137
+ }
138
+ }
139
+ return Object.freeze({ index, kind: page.kind, width: page.usedWidth, height: page.usedHeight, bytesPerPixel: bytesPerPixel, pixels });
140
+ }
141
+
142
+ const INSTANCE_BYTES = 144;
143
+ const INSTANCE_WORDS = INSTANCE_BYTES / 4;
144
+ const WHITE = [1, 1, 1, 1];
145
+ const IDENTITY_COLOR = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0];
146
+ const compareCommands = (a, b) => (a.command.priority ?? 0) - (b.command.priority ?? 0) || a.sourceOrder - b.sourceOrder;
147
+ class IndexedSpriteRenderer {
148
+ layout;
149
+ limits;
150
+ targetFormat;
151
+ label;
152
+ sampleCount;
153
+ #device;
154
+ #pipelines = new Map();
155
+ #pages = [];
156
+ #paletteTexture = null;
157
+ #dummyIndexTexture = null;
158
+ #dummyColorTexture = null;
159
+ #nearestSampler = null;
160
+ #linearSampler = null;
161
+ #frameBindGroup = null;
162
+ #viewportBuffer = null;
163
+ #instanceBuffer = null;
164
+ #uploadJobs = [];
165
+ #uploadedBytes = 0;
166
+ #drawCommands = 0;
167
+ #drawCalls = 0;
168
+ #batches = 0;
169
+ #generation = 0;
170
+ #disposed = false;
171
+ #prepared = [];
172
+ #commandPool = [];
173
+ #instanceData = new ArrayBuffer(0);
174
+ #instanceFloats = new Float32Array(this.#instanceData);
175
+ #instanceUints = new Uint32Array(this.#instanceData);
176
+ #viewportData = new Float32Array(4);
177
+ constructor(device, sprites, palettes, options) {
178
+ this.#device = device;
179
+ this.targetFormat = options.targetFormat;
180
+ this.limits = options.limits ?? DEFAULT_INDEXED_SPRITE_ATLAS_LIMITS;
181
+ this.label = options.label ?? 'IndexedSpriteRenderer';
182
+ this.sampleCount = options.sampleCount ?? 1;
183
+ this.layout = prepareIndexedSpriteAtlas(sprites, palettes, this.limits);
184
+ this.#createGpuResources();
185
+ }
186
+ get ready() { return !this.#disposed && this.#uploadJobs.length === 0; }
187
+ upload(maxBytes = this.limits.maxUploadBytesPerFrame) {
188
+ this.#assertAlive();
189
+ if (!Number.isSafeInteger(maxBytes) || maxBytes < 1 || maxBytes > this.limits.maxUploadBytesPerFrame)
190
+ throw new RangeError(`Indexed sprite upload budget must be between 1 and ${this.limits.maxUploadBytesPerFrame}.`);
191
+ let remaining = maxBytes;
192
+ while (remaining > 0 && this.#uploadJobs.length > 0) {
193
+ const job = this.#uploadJobs[0];
194
+ const sourceBytesPerRow = job.width * job.bytesPerPixel;
195
+ const alignedBytesPerRow = align(sourceBytesPerRow, 256);
196
+ if (alignedBytesPerRow > maxBytes)
197
+ throw new RangeError(`One indexed sprite atlas row requires ${alignedBytesPerRow} upload bytes, exceeding maxUploadBytesPerFrame=${maxBytes}.`);
198
+ const rows = Math.min(job.height - job.nextRow, Math.floor(remaining / alignedBytesPerRow));
199
+ if (rows < 1)
200
+ break;
201
+ const upload = new Uint8Array(alignedBytesPerRow * rows);
202
+ for (let row = 0; row < rows; row++) {
203
+ const sourceOffset = (job.nextRow + row) * sourceBytesPerRow;
204
+ upload.set(job.pixels.subarray(sourceOffset, sourceOffset + sourceBytesPerRow), row * alignedBytesPerRow);
205
+ }
206
+ this.#device.queue.writeTexture({ texture: job.texture, origin: { x: 0, y: job.nextRow, z: 0 } }, upload, { offset: 0, bytesPerRow: alignedBytesPerRow, rowsPerImage: rows }, { width: job.width, height: rows, depthOrArrayLayers: 1 });
207
+ const sourceUploaded = sourceBytesPerRow * rows;
208
+ this.#uploadedBytes += sourceUploaded;
209
+ remaining -= alignedBytesPerRow * rows;
210
+ job.nextRow += rows;
211
+ if (job.nextRow === job.height)
212
+ this.#uploadJobs.shift();
213
+ }
214
+ return this.stats();
215
+ }
216
+ uploadAll() {
217
+ while (!this.ready)
218
+ this.upload(this.limits.maxUploadBytesPerFrame);
219
+ return this.stats();
220
+ }
221
+ render(pass, commands, viewportWidth, viewportHeight) {
222
+ this.#assertAlive();
223
+ if (!this.ready)
224
+ throw new Error('Indexed sprite renderer cannot draw until all bounded uploads are complete.');
225
+ if (!Number.isFinite(viewportWidth) || !Number.isFinite(viewportHeight) || viewportWidth <= 0 || viewportHeight <= 0)
226
+ throw new RangeError('Indexed sprite viewport must be finite and positive.');
227
+ if (commands.length > this.limits.maxDrawCommandsPerFrame)
228
+ throw new RangeError(`Indexed sprite draw command count exceeds ${this.limits.maxDrawCommandsPerFrame}.`);
229
+ const prepared = this.#prepared;
230
+ const bytes = commands.length * INSTANCE_BYTES;
231
+ if (bytes > this.#instanceData.byteLength) {
232
+ const capacity = Math.min(this.limits.maxDrawCommandsPerFrame, Math.max(64, 2 ** Math.ceil(Math.log2(commands.length))));
233
+ this.#instanceData = new ArrayBuffer(capacity * INSTANCE_BYTES);
234
+ this.#instanceFloats = new Float32Array(this.#instanceData);
235
+ this.#instanceUints = new Uint32Array(this.#instanceData);
236
+ }
237
+ try {
238
+ let ordered = true, previousPriority = -Infinity;
239
+ for (let i = 0; i < commands.length; i++) {
240
+ const value = this.#prepareCommand(commands[i], i);
241
+ prepared.push(value);
242
+ const priority = commands[i].priority ?? 0;
243
+ if (priority < previousPriority)
244
+ ordered = false;
245
+ previousPriority = priority;
246
+ }
247
+ if (!ordered)
248
+ prepared.sort(compareCommands);
249
+ for (let i = 0; i < prepared.length; i++)
250
+ this.#writeInstance(prepared[i], i * INSTANCE_WORDS, this.#instanceFloats, this.#instanceUints);
251
+ // writeBuffer copies immediately; CPU scratch can be reused after this call.
252
+ if (bytes > 0)
253
+ this.#device.queue.writeBuffer(this.#instanceBuffer, 0, this.#instanceData, 0, bytes);
254
+ this.#viewportData[0] = viewportWidth;
255
+ this.#viewportData[1] = viewportHeight;
256
+ this.#device.queue.writeBuffer(this.#viewportBuffer, 0, this.#viewportData);
257
+ pass.setBindGroup(0, this.#frameBindGroup);
258
+ let batchStart = 0;
259
+ let drawCalls = 0;
260
+ while (batchStart < prepared.length) {
261
+ const first = prepared[batchStart];
262
+ let batchEnd = batchStart + 1;
263
+ while (batchEnd < prepared.length) {
264
+ const next = prepared[batchEnd];
265
+ if (next.pageIndex !== first.pageIndex || next.sampling !== first.sampling || next.blend !== first.blend)
266
+ break;
267
+ batchEnd++;
268
+ }
269
+ const page = this.#pages[first.pageIndex];
270
+ pass.setPipeline(this.#pipelines.get(first.blend));
271
+ pass.setBindGroup(1, first.sampling === 'linear' ? page.linearBindGroup : page.nearestBindGroup);
272
+ pass.draw(6, batchEnd - batchStart, 0, batchStart);
273
+ drawCalls++;
274
+ batchStart = batchEnd;
275
+ }
276
+ this.#drawCommands = prepared.length;
277
+ this.#drawCalls = drawCalls;
278
+ this.#batches = drawCalls;
279
+ return this.stats();
280
+ }
281
+ finally {
282
+ for (const value of prepared)
283
+ value.command = null;
284
+ prepared.length = 0;
285
+ }
286
+ }
287
+ recover(device) {
288
+ this.#assertAlive();
289
+ this.#destroyGpuResources();
290
+ this.#device = device;
291
+ this.#generation++;
292
+ this.#uploadedBytes = 0;
293
+ this.#createGpuResources();
294
+ }
295
+ stats() {
296
+ const pendingUploadBytes = Math.max(0, this.layout.gpuBytes - this.#uploadedBytes);
297
+ return Object.freeze({
298
+ pageCount: this.layout.pages.length,
299
+ indexedPageCount: this.layout.pages.filter(page => page.kind === 'indexed').length,
300
+ colorPageCount: this.layout.pages.filter(page => page.kind === 'color').length,
301
+ paletteCount: this.layout.paletteRows.size,
302
+ cpuBytes: this.layout.cpuBytes,
303
+ gpuBytes: this.layout.gpuBytes,
304
+ uploadedBytes: this.#uploadedBytes,
305
+ pendingUploadBytes,
306
+ textureCount: this.#disposed ? 0 : this.#pages.length + 3,
307
+ drawCommands: this.#drawCommands,
308
+ drawCalls: this.#drawCalls,
309
+ batches: this.#batches,
310
+ generation: this.#generation,
311
+ ready: this.ready,
312
+ disposed: this.#disposed,
313
+ });
314
+ }
315
+ dispose() {
316
+ if (this.#disposed)
317
+ return;
318
+ this.#disposed = true;
319
+ this.#destroyGpuResources();
320
+ this.#prepared.length = 0;
321
+ this.#commandPool.length = 0;
322
+ this.#instanceData = new ArrayBuffer(0);
323
+ this.#instanceFloats = new Float32Array(this.#instanceData);
324
+ this.#instanceUints = new Uint32Array(this.#instanceData);
325
+ }
326
+ #createGpuResources() {
327
+ const maximumDimension = this.#device.limits.maxTextureDimension2D;
328
+ for (const page of this.layout.pages)
329
+ if (page.width > maximumDimension || page.height > maximumDimension)
330
+ throw new RangeError(`Indexed sprite atlas page ${page.index} exceeds the device texture limit ${maximumDimension}.`);
331
+ if (this.layout.paletteWidth > maximumDimension || this.layout.paletteHeight > maximumDimension)
332
+ throw new RangeError('Indexed sprite palette bank exceeds the device texture limit.');
333
+ const module = this.#device.createShaderModule({ label: `${this.label}.shader`, code: IndexedSpriteWgsl });
334
+ const frameLayout = this.#device.createBindGroupLayout({
335
+ label: `${this.label}.frameLayout`,
336
+ entries: [
337
+ { binding: 0, visibility: GPUShaderStage.VERTEX, buffer: { type: 'uniform' } },
338
+ { binding: 1, visibility: GPUShaderStage.VERTEX, buffer: { type: 'read-only-storage' } },
339
+ ],
340
+ });
341
+ const materialLayout = this.#device.createBindGroupLayout({
342
+ label: `${this.label}.materialLayout`,
343
+ entries: [
344
+ { binding: 0, visibility: GPUShaderStage.FRAGMENT, texture: { sampleType: 'uint' } },
345
+ { binding: 1, visibility: GPUShaderStage.FRAGMENT, texture: { sampleType: 'float' } },
346
+ { binding: 2, visibility: GPUShaderStage.FRAGMENT, texture: { sampleType: 'float' } },
347
+ { binding: 3, visibility: GPUShaderStage.FRAGMENT, sampler: { type: 'filtering' } },
348
+ ],
349
+ });
350
+ const pipelineLayout = this.#device.createPipelineLayout({ label: `${this.label}.pipelineLayout`, bindGroupLayouts: [frameLayout, materialLayout] });
351
+ for (const blend of ['alpha', 'additive', 'subtractive', 'opaque'])
352
+ this.#pipelines.set(blend, this.#device.createRenderPipeline({
353
+ label: `${this.label}.pipeline.${blend}`,
354
+ layout: pipelineLayout,
355
+ vertex: { module, entryPoint: 'vs_main' },
356
+ fragment: { module, entryPoint: 'fs_main', targets: [colorTarget(this.targetFormat, blend)] },
357
+ primitive: { topology: 'triangle-list', cullMode: 'none' },
358
+ multisample: { count: this.sampleCount },
359
+ }));
360
+ this.#viewportBuffer = this.#device.createBuffer({ label: `${this.label}.viewport`, size: 16, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
361
+ this.#instanceBuffer = this.#device.createBuffer({ label: `${this.label}.instances`, size: this.limits.maxDrawCommandsPerFrame * INSTANCE_BYTES, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST });
362
+ this.#frameBindGroup = this.#device.createBindGroup({ label: `${this.label}.frame`, layout: frameLayout, entries: [{ binding: 0, resource: { buffer: this.#viewportBuffer } }, { binding: 1, resource: { buffer: this.#instanceBuffer } }] });
363
+ this.#nearestSampler = this.#device.createSampler({ label: `${this.label}.nearest`, magFilter: 'nearest', minFilter: 'nearest', mipmapFilter: 'nearest', addressModeU: 'clamp-to-edge', addressModeV: 'clamp-to-edge' });
364
+ this.#linearSampler = this.#device.createSampler({ label: `${this.label}.linear`, magFilter: 'linear', minFilter: 'linear', mipmapFilter: 'nearest', addressModeU: 'clamp-to-edge', addressModeV: 'clamp-to-edge' });
365
+ this.#dummyIndexTexture = createTexture(this.#device, `${this.label}.dummyIndex`, 1, 1, 'r8uint');
366
+ this.#dummyColorTexture = createTexture(this.#device, `${this.label}.dummyColor`, 1, 1, 'rgba8unorm');
367
+ this.#paletteTexture = createTexture(this.#device, `${this.label}.palettes`, this.layout.paletteWidth, this.layout.paletteHeight, 'rgba8unorm');
368
+ this.#uploadJobs.push({ texture: this.#paletteTexture, width: this.layout.paletteWidth, height: this.layout.paletteHeight, bytesPerPixel: 4, pixels: this.layout.palettePixels, nextRow: 0 });
369
+ for (const page of this.layout.pages) {
370
+ const texture = createTexture(this.#device, `${this.label}.${page.kind}.${page.index}`, page.width, page.height, page.kind === 'indexed' ? 'r8uint' : 'rgba8unorm');
371
+ this.#uploadJobs.push({ texture, width: page.width, height: page.height, bytesPerPixel: page.bytesPerPixel, pixels: page.pixels, nextRow: 0 });
372
+ const entriesWithoutSampler = [
373
+ { binding: 0, resource: (page.kind === 'indexed' ? texture : this.#dummyIndexTexture).createView() },
374
+ { binding: 1, resource: (page.kind === 'color' ? texture : this.#dummyColorTexture).createView() },
375
+ { binding: 2, resource: this.#paletteTexture.createView() },
376
+ ];
377
+ const makeBindGroup = (sampling, sampler) => this.#device.createBindGroup({ label: `${this.label}.${page.index}.${sampling}`, layout: materialLayout, entries: [...entriesWithoutSampler, { binding: 3, resource: sampler }] });
378
+ this.#pages.push({ page, texture, nearestBindGroup: makeBindGroup('nearest', this.#nearestSampler), linearBindGroup: makeBindGroup('linear', this.#linearSampler) });
379
+ }
380
+ }
381
+ #prepareCommand(command, sourceOrder) {
382
+ const placement = this.layout.placements.get(command.spriteId);
383
+ if (!placement)
384
+ throw new RangeError(`Indexed sprite draw references unknown sprite ${command.spriteId}.`);
385
+ if (placement.pageKind === 'indexed' && (!command.paletteId || !this.layout.paletteRows.has(command.paletteId)))
386
+ throw new RangeError(`Indexed sprite ${command.spriteId} requires a known paletteId.`);
387
+ if (!Number.isFinite(command.x) || !Number.isFinite(command.y) || !Number.isFinite(command.axisX ?? 0) || !Number.isFinite(command.axisY ?? 0) || !Number.isFinite(command.scaleX ?? 1) || !Number.isFinite(command.scaleY ?? 1) || !Number.isFinite(command.rotationRadians ?? 0) || !Number.isFinite(command.opacity ?? 1) || !Number.isFinite(command.priority ?? 0) || !Number.isFinite(command.depth ?? 0.5))
388
+ throw new RangeError('Indexed sprite draw contains a non-finite number.');
389
+ const opacity = command.opacity ?? 1;
390
+ if (opacity < 0 || opacity > 1)
391
+ throw new RangeError('Indexed sprite opacity must be in [0,1].');
392
+ const value = this.#commandPool[sourceOrder] ??= { command: null, pageIndex: 0, pageKind: 'color', sampling: 'nearest', blend: 'alpha', sourceOrder };
393
+ value.command = command;
394
+ value.pageIndex = placement.pageIndex;
395
+ value.pageKind = placement.pageKind;
396
+ value.sampling = command.sampling ?? 'nearest';
397
+ value.blend = command.blend ?? 'alpha';
398
+ return value;
399
+ }
400
+ #writeInstance(prepared, offset, floats, uints) {
401
+ const command = prepared.command;
402
+ const placement = this.layout.placements.get(command.spriteId);
403
+ floats[offset] = command.x;
404
+ floats[offset + 1] = command.y;
405
+ floats[offset + 2] = command.scaleX ?? 1;
406
+ floats[offset + 3] = command.scaleY ?? 1;
407
+ floats[offset + 4] = placement.width;
408
+ floats[offset + 5] = placement.height;
409
+ floats[offset + 6] = command.axisX ?? 0;
410
+ floats[offset + 7] = command.axisY ?? 0;
411
+ floats[offset + 8] = placement.x;
412
+ floats[offset + 9] = placement.y;
413
+ floats[offset + 10] = placement.width;
414
+ floats[offset + 11] = placement.height;
415
+ floats[offset + 12] = command.rotationRadians ?? 0;
416
+ floats[offset + 13] = command.depth ?? 0.5;
417
+ floats[offset + 14] = command.opacity ?? 1;
418
+ floats[offset + 15] = 0;
419
+ uints[offset + 16] = prepared.pageKind === 'indexed' ? this.layout.paletteRows.get(command.paletteId) : 0;
420
+ uints[offset + 17] = (prepared.pageKind === 'indexed' ? 1 : 0) | (prepared.sampling === 'linear' ? 2 : 0) | (command.flipX ? 4 : 0) | (command.flipY ? 8 : 0);
421
+ uints[offset + 18] = 0;
422
+ uints[offset + 19] = 0;
423
+ const tint = command.tint ?? WHITE;
424
+ for (let index = 0; index < 4; index++) {
425
+ const value = tint[index];
426
+ if (!Number.isFinite(value))
427
+ throw new RangeError('Indexed sprite tint must be finite.');
428
+ floats[offset + 20 + index] = value;
429
+ }
430
+ const colorMatrix = command.colorMatrix ?? IDENTITY_COLOR;
431
+ if (colorMatrix.length !== 12)
432
+ throw new RangeError('Indexed sprite colorMatrix must contain twelve values.');
433
+ for (let index = 0; index < 12; index++) {
434
+ const value = colorMatrix[index];
435
+ if (!Number.isFinite(value))
436
+ throw new RangeError('Indexed sprite colorMatrix must be finite.');
437
+ floats[offset + 24 + index] = value;
438
+ }
439
+ }
440
+ #destroyGpuResources() {
441
+ for (const page of this.#pages)
442
+ page.texture.destroy();
443
+ this.#paletteTexture?.destroy();
444
+ this.#dummyIndexTexture?.destroy();
445
+ this.#dummyColorTexture?.destroy();
446
+ this.#viewportBuffer?.destroy();
447
+ this.#instanceBuffer?.destroy();
448
+ this.#pages = [];
449
+ this.#pipelines.clear();
450
+ this.#uploadJobs = [];
451
+ this.#paletteTexture = null;
452
+ this.#dummyIndexTexture = null;
453
+ this.#dummyColorTexture = null;
454
+ this.#nearestSampler = null;
455
+ this.#linearSampler = null;
456
+ this.#frameBindGroup = null;
457
+ this.#viewportBuffer = null;
458
+ this.#instanceBuffer = null;
459
+ }
460
+ #assertAlive() { if (this.#disposed)
461
+ throw new Error('Indexed sprite renderer is disposed.'); }
462
+ }
463
+ function createTexture(device, label, width, height, format) { return device.createTexture({ label, size: { width, height, depthOrArrayLayers: 1 }, format, usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST }); }
464
+ function align(value, alignment) { return Math.ceil(value / alignment) * alignment; }
465
+ function blendState(blend) {
466
+ if (blend === 'opaque')
467
+ return undefined;
468
+ if (blend === 'additive')
469
+ return { color: { operation: 'add', srcFactor: 'src-alpha', dstFactor: 'one' }, alpha: { operation: 'add', srcFactor: 'one', dstFactor: 'one' } };
470
+ if (blend === 'subtractive')
471
+ return { color: { operation: 'reverse-subtract', srcFactor: 'src-alpha', dstFactor: 'one' }, alpha: { operation: 'add', srcFactor: 'zero', dstFactor: 'one' } };
472
+ return { color: { operation: 'add', srcFactor: 'src-alpha', dstFactor: 'one-minus-src-alpha' }, alpha: { operation: 'add', srcFactor: 'one', dstFactor: 'one-minus-src-alpha' } };
473
+ }
474
+ function colorTarget(format, blend) {
475
+ const state = blendState(blend);
476
+ return state
477
+ ? { format, blend: state, writeMask: GPUColorWrite.ALL }
478
+ : { format, writeMask: GPUColorWrite.ALL };
479
+ }
480
+
481
+ export { DEFAULT_INDEXED_SPRITE_ATLAS_LIMITS, IndexedSpriteRenderer, prepareIndexedSpriteAtlas };
482
+ //# sourceMappingURL=experimental-indexed-sprite.js.map
@@ -5977,15 +5977,6 @@ mat4} = wgpuMatrixAPI(Float32Array, Float32Array, Float32Array, Float32Array, Fl
5977
5977
  wgpuMatrixAPI(Float64Array, Float64Array, Float64Array, Float64Array, Float64Array, Float64Array);
5978
5978
  wgpuMatrixAPI(ZeroArray, Array, Array, Array, Array, Array);
5979
5979
 
5980
- function monotonicNow() {
5981
- return globalThis.performance?.now() ?? Date.now();
5982
- }
5983
- function createAbortError(message, cause) {
5984
- const error = new Error(message, { cause });
5985
- error.name = 'AbortError';
5986
- return error;
5987
- }
5988
-
5989
5980
  /** Asset semantics supported by the metallic-roughness pipeline. */
5990
5981
  const PBR_COMPATIBILITY_CONTRACT = Object.freeze({
5991
5982
  supportedUvSets: Object.freeze([0, 1]),
@@ -6012,6 +6003,15 @@ const PBR_COMPATIBILITY_CONTRACT = Object.freeze({
6012
6003
  }),
6013
6004
  });
6014
6005
 
6006
+ function monotonicNow() {
6007
+ return globalThis.performance?.now() ?? Date.now();
6008
+ }
6009
+ function createAbortError(message, cause) {
6010
+ const error = new Error(message, { cause });
6011
+ error.name = 'AbortError';
6012
+ return error;
6013
+ }
6014
+
6015
6015
  /** Validates the untrusted JSON envelope before the loader follows references. */
6016
6016
  function isGltfAsset(value) {
6017
6017
  if (!isRecord(value))
@@ -2,9 +2,10 @@ import { hyaStateMachineChannelCapability, HYA_STATE_MACHINE_EXTENSION_ID, parse
2
2
  export { HYA_STATE_MACHINE_CHANNEL_REGISTRY, hyaStateMachineChannelCapability } from '@haiyue/animation-spec';
3
3
  import { Component, System } from '@haiyue/engine';
4
4
  import { UniqueCheckType, ComponentLifecycleFlags } from '@haiyue/engine/ecs';
5
- import { r as runAnimationStateMachineControllerUpdateTransaction } from './chunks/AnimationStateMachineController-BIcAh-sT.js';
6
- import { A as Animation2DRuntime, c as createAnimation2DStateMachineController } from './chunks/Animation2DStateMachine-9IonguNa.js';
5
+ import { r as runAnimationStateMachineControllerUpdateTransaction } from './chunks/AnimationStateMachineController-4sKaqh9E.js';
6
+ import { A as Animation2DRuntime, c as createAnimation2DStateMachineController } from './chunks/Animation2DStateMachine-BHcA6Cu3.js';
7
7
  import '@haiyue/engine/geometry';
8
+ import './chunks/AnimationVisual2D-8y3qp0_U.js';
8
9
  import '@haiyue/engine/components';
9
10
 
10
11
  class Animation2DStateMachineActionRuntimeHandle {
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { R as RenderSystem2DBase } from './chunks/RenderSystem2DBase-DDT61dAu.js';
1
+ export { R as RenderSystem2DBase } from './chunks/RenderSystem2DBase-CF2s00vg.js';
2
2
  import '@haiyue/engine/extension-authoring';
3
3
  import '@haiyue/engine/components';
4
4
  import '@haiyue/engine/ecs';
@@ -0,0 +1,2 @@
1
+ import { type IndexedSpriteAtlasLayout, type IndexedSpriteAtlasLimits, type IndexedSpritePaletteDescriptor, type IndexedSpritePlaneDescriptor } from './contracts';
2
+ export declare function prepareIndexedSpriteAtlas(sprites: readonly IndexedSpritePlaneDescriptor[], palettes: readonly IndexedSpritePaletteDescriptor[], limits?: IndexedSpriteAtlasLimits): IndexedSpriteAtlasLayout;
@@ -0,0 +1,23 @@
1
+ import { type IndexedSpriteAtlasLayout, type IndexedSpriteAtlasLimits, type IndexedSpriteDrawCommand, type IndexedSpritePaletteDescriptor, type IndexedSpritePlaneDescriptor, type IndexedSpriteRendererStats } from './contracts';
2
+ export interface IndexedSpriteRendererOptions {
3
+ readonly targetFormat: GPUTextureFormat;
4
+ readonly limits?: IndexedSpriteAtlasLimits;
5
+ readonly label?: string;
6
+ readonly sampleCount?: 1 | 4;
7
+ }
8
+ export declare class IndexedSpriteRenderer {
9
+ #private;
10
+ readonly layout: IndexedSpriteAtlasLayout;
11
+ readonly limits: IndexedSpriteAtlasLimits;
12
+ readonly targetFormat: GPUTextureFormat;
13
+ readonly label: string;
14
+ readonly sampleCount: 1 | 4;
15
+ constructor(device: GPUDevice, sprites: readonly IndexedSpritePlaneDescriptor[], palettes: readonly IndexedSpritePaletteDescriptor[], options: IndexedSpriteRendererOptions);
16
+ get ready(): boolean;
17
+ upload(maxBytes?: number): IndexedSpriteRendererStats;
18
+ uploadAll(): IndexedSpriteRendererStats;
19
+ render(pass: GPURenderPassEncoder, commands: readonly IndexedSpriteDrawCommand[], viewportWidth: number, viewportHeight: number): IndexedSpriteRendererStats;
20
+ recover(device: GPUDevice): void;
21
+ stats(): IndexedSpriteRendererStats;
22
+ dispose(): void;
23
+ }
@@ -0,0 +1,89 @@
1
+ export type IndexedSpritePlaneFormat = 'indexed8' | 'rgb8' | 'rgba8';
2
+ export type IndexedSpriteSampling = 'nearest' | 'linear';
3
+ export type IndexedSpriteBlend = 'alpha' | 'additive' | 'subtractive' | 'opaque';
4
+ export interface IndexedSpritePlaneDescriptor {
5
+ readonly id: string;
6
+ readonly width: number;
7
+ readonly height: number;
8
+ readonly format: IndexedSpritePlaneFormat;
9
+ readonly pixels: Uint8Array;
10
+ }
11
+ export interface IndexedSpritePaletteDescriptor {
12
+ readonly id: string;
13
+ readonly colorCount: number;
14
+ readonly rgba: Uint8Array;
15
+ }
16
+ export interface IndexedSpriteAtlasLimits {
17
+ readonly maxTextureDimension2D: number;
18
+ readonly maxAtlasPages: number;
19
+ readonly maxGpuBytes: number;
20
+ readonly maxPaletteGpuBytes: number;
21
+ readonly maxUploadBytesPerFrame: number;
22
+ readonly maxDrawCommandsPerFrame: number;
23
+ }
24
+ export declare const DEFAULT_INDEXED_SPRITE_ATLAS_LIMITS: IndexedSpriteAtlasLimits;
25
+ export interface IndexedSpriteAtlasPlacement {
26
+ readonly spriteId: string;
27
+ readonly pageIndex: number;
28
+ readonly pageKind: 'indexed' | 'color';
29
+ readonly x: number;
30
+ readonly y: number;
31
+ readonly width: number;
32
+ readonly height: number;
33
+ }
34
+ export interface IndexedSpriteAtlasPage {
35
+ readonly index: number;
36
+ readonly kind: 'indexed' | 'color';
37
+ readonly width: number;
38
+ readonly height: number;
39
+ readonly bytesPerPixel: 1 | 4;
40
+ readonly pixels: Uint8Array;
41
+ }
42
+ export interface IndexedSpriteAtlasLayout {
43
+ readonly pages: readonly IndexedSpriteAtlasPage[];
44
+ readonly placements: ReadonlyMap<string, IndexedSpriteAtlasPlacement>;
45
+ readonly paletteRows: ReadonlyMap<string, number>;
46
+ readonly paletteWidth: 256;
47
+ readonly paletteHeight: number;
48
+ readonly palettePixels: Uint8Array;
49
+ readonly cpuBytes: number;
50
+ readonly gpuBytes: number;
51
+ }
52
+ export interface IndexedSpriteDrawCommand {
53
+ readonly spriteId: string;
54
+ readonly paletteId?: string;
55
+ readonly x: number;
56
+ readonly y: number;
57
+ readonly axisX?: number;
58
+ readonly axisY?: number;
59
+ readonly scaleX?: number;
60
+ readonly scaleY?: number;
61
+ readonly rotationRadians?: number;
62
+ readonly opacity?: number;
63
+ readonly tint?: readonly [number, number, number, number];
64
+ /** Row-major RGB affine transform: three vec4 rows containing rgb coefficients and additive bias. */
65
+ readonly colorMatrix?: readonly [number, number, number, number, number, number, number, number, number, number, number, number];
66
+ readonly flipX?: boolean;
67
+ readonly flipY?: boolean;
68
+ readonly sampling?: IndexedSpriteSampling;
69
+ readonly blend?: IndexedSpriteBlend;
70
+ readonly priority?: number;
71
+ readonly depth?: number;
72
+ }
73
+ export interface IndexedSpriteRendererStats {
74
+ readonly pageCount: number;
75
+ readonly indexedPageCount: number;
76
+ readonly colorPageCount: number;
77
+ readonly paletteCount: number;
78
+ readonly cpuBytes: number;
79
+ readonly gpuBytes: number;
80
+ readonly uploadedBytes: number;
81
+ readonly pendingUploadBytes: number;
82
+ readonly textureCount: number;
83
+ readonly drawCommands: number;
84
+ readonly drawCalls: number;
85
+ readonly batches: number;
86
+ readonly generation: number;
87
+ readonly ready: boolean;
88
+ readonly disposed: boolean;
89
+ }