@bloopjs/toodle 0.0.100

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 (358) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +44 -0
  3. package/dist/Toodle.d.ts +304 -0
  4. package/dist/Toodle.d.ts.map +1 -0
  5. package/dist/colors/mod.d.ts +872 -0
  6. package/dist/colors/mod.d.ts.map +1 -0
  7. package/dist/coreTypes/Color.d.ts +7 -0
  8. package/dist/coreTypes/Color.d.ts.map +1 -0
  9. package/dist/coreTypes/Point.d.ts +8 -0
  10. package/dist/coreTypes/Point.d.ts.map +1 -0
  11. package/dist/coreTypes/Size.d.ts +5 -0
  12. package/dist/coreTypes/Size.d.ts.map +1 -0
  13. package/dist/coreTypes/Transform.d.ts +16 -0
  14. package/dist/coreTypes/Transform.d.ts.map +1 -0
  15. package/dist/coreTypes/Vec2.d.ts +8 -0
  16. package/dist/coreTypes/Vec2.d.ts.map +1 -0
  17. package/dist/coreTypes/mod.d.ts +6 -0
  18. package/dist/coreTypes/mod.d.ts.map +1 -0
  19. package/dist/docs/snippets/add-remove-children.d.ts +1 -0
  20. package/dist/docs/snippets/basic-quad.d.ts +1 -0
  21. package/dist/docs/snippets/filter-linear.d.ts +1 -0
  22. package/dist/docs/snippets/filter-nearest.d.ts +1 -0
  23. package/dist/docs/snippets/flipxy.d.ts +1 -0
  24. package/dist/docs/snippets/hello-text.d.ts +1 -0
  25. package/dist/docs/snippets/jumbo-textures.d.ts +1 -0
  26. package/dist/docs/snippets/layer.d.ts +1 -0
  27. package/dist/docs/snippets/layout-edges.d.ts +1 -0
  28. package/dist/docs/snippets/layout-screen-and-world-space.d.ts +1 -0
  29. package/dist/docs/snippets/postprocess.d.ts +1 -0
  30. package/dist/docs/snippets/quad-size-scale.d.ts +1 -0
  31. package/dist/docs/snippets/quickstart.d.ts +1 -0
  32. package/dist/docs/snippets/repeat-texture-loading.d.ts +1 -0
  33. package/dist/docs/snippets/screen-shaders.d.ts +1 -0
  34. package/dist/docs/snippets/shader-color-flash.d.ts +1 -0
  35. package/dist/docs/snippets/shader-default.d.ts +1 -0
  36. package/dist/docs/snippets/shader-fill.d.ts +1 -0
  37. package/dist/docs/snippets/shapes-line.d.ts +1 -0
  38. package/dist/docs/snippets/sprite-region.d.ts +1 -0
  39. package/dist/docs/snippets/text-alignment.d.ts +1 -0
  40. package/dist/docs/snippets/text-shrink-to-fit.d.ts +1 -0
  41. package/dist/docs/snippets/text-word-wrap.d.ts +1 -0
  42. package/dist/docs/snippets/texture-bundles-prebaked.d.ts +1 -0
  43. package/dist/docs/snippets/texture-bundles.d.ts +1 -0
  44. package/dist/docs/snippets/transforms.d.ts +1 -0
  45. package/dist/docs/snippets/transparent-cropping.d.ts +1 -0
  46. package/dist/examples/0-hello.d.ts +1 -0
  47. package/dist/examples/1-hello.d.ts +1 -0
  48. package/dist/examples/1-hello.d.ts.map +1 -0
  49. package/dist/examples/1-quad.d.ts +1 -0
  50. package/dist/examples/10-resize.d.ts +1 -0
  51. package/dist/examples/10-resize.d.ts.map +1 -0
  52. package/dist/examples/11-bundle-test.d.ts +1 -0
  53. package/dist/examples/11-bundle-test.d.ts.map +1 -0
  54. package/dist/examples/12-transparent-pixel-cropping.d.ts +1 -0
  55. package/dist/examples/12-transparent-pixel-cropping.d.ts.map +1 -0
  56. package/dist/examples/13-crop.d.ts +1 -0
  57. package/dist/examples/13-crop.d.ts.map +1 -0
  58. package/dist/examples/14-bundle-bench.d.ts +1 -0
  59. package/dist/examples/14-bundle-bench.d.ts.map +1 -0
  60. package/dist/examples/15-text-layer.d.ts +1 -0
  61. package/dist/examples/15-text-layer.d.ts.map +1 -0
  62. package/dist/examples/16-jumbo-texture.d.ts +1 -0
  63. package/dist/examples/16-jumbo-textures.d.ts +1 -0
  64. package/dist/examples/16-screen-shader.d.ts +1 -0
  65. package/dist/examples/16-screen-shader.d.ts.map +1 -0
  66. package/dist/examples/17-lighting.d.ts +1 -0
  67. package/dist/examples/17-lighting.d.ts.map +1 -0
  68. package/dist/examples/17-translations.d.ts +1 -0
  69. package/dist/examples/18-blur.d.ts +1 -0
  70. package/dist/examples/19-postprocess.d.ts +1 -0
  71. package/dist/examples/19-screenshader.d.ts +1 -0
  72. package/dist/examples/2-shapes.d.ts +1 -0
  73. package/dist/examples/2-shapes.d.ts.map +1 -0
  74. package/dist/examples/3-shader.d.ts +1 -0
  75. package/dist/examples/3-shader.d.ts.map +1 -0
  76. package/dist/examples/4-shader-bench.d.ts +1 -0
  77. package/dist/examples/4-shader-bench.d.ts.map +1 -0
  78. package/dist/examples/5-z.d.ts +1 -0
  79. package/dist/examples/5-z.d.ts.map +1 -0
  80. package/dist/examples/6-atlas.d.ts +1 -0
  81. package/dist/examples/6-atlas.d.ts.map +1 -0
  82. package/dist/examples/7-text.d.ts +1 -0
  83. package/dist/examples/7-text.d.ts.map +1 -0
  84. package/dist/examples/8-text-bench.d.ts +1 -0
  85. package/dist/examples/8-text-bench.d.ts.map +1 -0
  86. package/dist/examples/9-alignment.d.ts +1 -0
  87. package/dist/examples/9-alignment.d.ts.map +1 -0
  88. package/dist/examples/main.d.ts +1 -0
  89. package/dist/examples/main.d.ts.map +1 -0
  90. package/dist/examples/util.d.ts +82 -0
  91. package/dist/examples/util.d.ts.map +1 -0
  92. package/dist/limits.d.ts +23 -0
  93. package/dist/limits.d.ts.map +1 -0
  94. package/dist/math/angle.d.ts +13 -0
  95. package/dist/math/angle.d.ts.map +1 -0
  96. package/dist/math/matrix.d.ts +26 -0
  97. package/dist/math/matrix.d.ts.map +1 -0
  98. package/dist/math/mod.d.ts +3 -0
  99. package/dist/math/mod.d.ts.map +1 -0
  100. package/dist/mod.d.ts +17 -0
  101. package/dist/mod.d.ts.map +1 -0
  102. package/dist/mod.js +19665 -0
  103. package/dist/mod.js.map +41 -0
  104. package/dist/postprocess.d.ts +10 -0
  105. package/dist/postprocess.d.ts.map +1 -0
  106. package/dist/scene/Batcher.d.ts +20 -0
  107. package/dist/scene/Batcher.d.ts.map +1 -0
  108. package/dist/scene/Camera.d.ts +16 -0
  109. package/dist/scene/Camera.d.ts.map +1 -0
  110. package/dist/scene/JumboQuadNode.d.ts +29 -0
  111. package/dist/scene/JumboQuadNode.d.ts.map +1 -0
  112. package/dist/scene/QuadNode.d.ts +159 -0
  113. package/dist/scene/QuadNode.d.ts.map +1 -0
  114. package/dist/scene/RenderComponent.d.ts +11 -0
  115. package/dist/scene/RenderComponent.d.ts.map +1 -0
  116. package/dist/scene/SceneNode.d.ts +300 -0
  117. package/dist/scene/SceneNode.d.ts.map +1 -0
  118. package/dist/scene/mod.d.ts +5 -0
  119. package/dist/scene/mod.d.ts.map +1 -0
  120. package/dist/screen/mod.d.ts +2 -0
  121. package/dist/screen/mod.d.ts.map +1 -0
  122. package/dist/screen/resolution.d.ts +5 -0
  123. package/dist/screen/resolution.d.ts.map +1 -0
  124. package/dist/shaders/EngineUniform.d.ts +9 -0
  125. package/dist/shaders/EngineUniform.d.ts.map +1 -0
  126. package/dist/shaders/IShader.d.ts +15 -0
  127. package/dist/shaders/IShader.d.ts.map +1 -0
  128. package/dist/shaders/QuadShader.d.ts +18 -0
  129. package/dist/shaders/QuadShader.d.ts.map +1 -0
  130. package/dist/shaders/ShaderDescriptor.d.ts +7 -0
  131. package/dist/shaders/ShaderDescriptor.d.ts.map +1 -0
  132. package/dist/shaders/mod.d.ts +6 -0
  133. package/dist/shaders/mod.d.ts.map +1 -0
  134. package/dist/shaders/parser.d.ts +8 -0
  135. package/dist/shaders/parser.d.ts.map +1 -0
  136. package/dist/shaders/postprocess/blur.d.ts +3 -0
  137. package/dist/shaders/postprocess/blur.d.ts.map +1 -0
  138. package/dist/shaders/postprocess/mod.d.ts +17 -0
  139. package/dist/shaders/postprocess/mod.d.ts.map +1 -0
  140. package/dist/shaders/samplers.d.ts +3 -0
  141. package/dist/shaders/samplers.d.ts.map +1 -0
  142. package/dist/shaders/wgsl/example.wgsl.d.ts +3 -0
  143. package/dist/shaders/wgsl/example.wgsl.d.ts.map +1 -0
  144. package/dist/shaders/wgsl/hello.wgsl.d.ts +3 -0
  145. package/dist/shaders/wgsl/hello.wgsl.d.ts.map +1 -0
  146. package/dist/shaders/wgsl/helloInstanced.wgsl.d.ts +3 -0
  147. package/dist/shaders/wgsl/helloInstanced.wgsl.d.ts.map +1 -0
  148. package/dist/shaders/wgsl/quad.wgsl.d.ts +3 -0
  149. package/dist/shaders/wgsl/quad.wgsl.d.ts.map +1 -0
  150. package/dist/src/Toodle.d.ts +303 -0
  151. package/dist/src/Toodle.d.ts.map +1 -0
  152. package/dist/src/colors/mod.d.ts +871 -0
  153. package/dist/src/coreTypes/Color.d.ts +6 -0
  154. package/dist/src/coreTypes/Color.d.ts.map +1 -0
  155. package/dist/src/coreTypes/Point.d.ts +7 -0
  156. package/dist/src/coreTypes/Point.d.ts.map +1 -0
  157. package/dist/src/coreTypes/Size.d.ts +4 -0
  158. package/dist/src/coreTypes/Size.d.ts.map +1 -0
  159. package/dist/src/coreTypes/Transform.d.ts +15 -0
  160. package/dist/src/coreTypes/Transform.d.ts.map +1 -0
  161. package/dist/src/coreTypes/Vec2.d.ts +7 -0
  162. package/dist/src/coreTypes/Vec2.d.ts.map +1 -0
  163. package/dist/src/coreTypes/mod.d.ts +5 -0
  164. package/dist/src/coreTypes/mod.d.ts.map +1 -0
  165. package/dist/src/limits.d.ts +22 -0
  166. package/dist/src/limits.d.ts.map +1 -0
  167. package/dist/src/math/angle.d.ts +12 -0
  168. package/dist/src/math/angle.d.ts.map +1 -0
  169. package/dist/src/math/matrix.d.ts +25 -0
  170. package/dist/src/math/matrix.d.ts.map +1 -0
  171. package/dist/src/math/mod.d.ts +2 -0
  172. package/dist/src/math/mod.d.ts.map +1 -0
  173. package/dist/src/mod.d.ts +16 -0
  174. package/dist/src/mod.d.ts.map +1 -0
  175. package/dist/src/postprocess.d.ts +10 -0
  176. package/dist/src/postprocess.d.ts.map +1 -0
  177. package/dist/src/scene/Batcher.d.ts +19 -0
  178. package/dist/src/scene/Batcher.d.ts.map +1 -0
  179. package/dist/src/scene/Camera.d.ts +15 -0
  180. package/dist/src/scene/Camera.d.ts.map +1 -0
  181. package/dist/src/scene/JumboQuadNode.d.ts +28 -0
  182. package/dist/src/scene/QuadNode.d.ts +158 -0
  183. package/dist/src/scene/QuadNode.d.ts.map +1 -0
  184. package/dist/src/scene/RenderComponent.d.ts +10 -0
  185. package/dist/src/scene/RenderComponent.d.ts.map +1 -0
  186. package/dist/src/scene/SceneNode.d.ts +299 -0
  187. package/dist/src/scene/SceneNode.d.ts.map +1 -0
  188. package/dist/src/scene/mod.d.ts +4 -0
  189. package/dist/src/scene/mod.d.ts.map +1 -0
  190. package/dist/src/screen/mod.d.ts +1 -0
  191. package/dist/src/screen/mod.d.ts.map +1 -0
  192. package/dist/src/screen/resolution.d.ts +4 -0
  193. package/dist/src/screen/resolution.d.ts.map +1 -0
  194. package/dist/src/shaders/EngineUniform.d.ts +8 -0
  195. package/dist/src/shaders/EngineUniform.d.ts.map +1 -0
  196. package/dist/src/shaders/IShader.d.ts +14 -0
  197. package/dist/src/shaders/IShader.d.ts.map +1 -0
  198. package/dist/src/shaders/QuadShader.d.ts +17 -0
  199. package/dist/src/shaders/QuadShader.d.ts.map +1 -0
  200. package/dist/src/shaders/ShaderDescriptor.d.ts +6 -0
  201. package/dist/src/shaders/ShaderDescriptor.d.ts.map +1 -0
  202. package/dist/src/shaders/mod.d.ts +5 -0
  203. package/dist/src/shaders/mod.d.ts.map +1 -0
  204. package/dist/src/shaders/parser.d.ts +7 -0
  205. package/dist/src/shaders/parser.d.ts.map +1 -0
  206. package/dist/src/shaders/postprocess/blur.d.ts +2 -0
  207. package/dist/src/shaders/postprocess/mod.d.ts +16 -0
  208. package/dist/src/shaders/postprocess/postprocess.d.ts +8 -0
  209. package/dist/src/shaders/postprocess/util.d.ts +2 -0
  210. package/dist/src/shaders/samplers.d.ts +2 -0
  211. package/dist/src/shaders/samplers.d.ts.map +1 -0
  212. package/dist/src/shaders/wgsl/example.wgsl.d.ts +2 -0
  213. package/dist/src/shaders/wgsl/example.wgsl.d.ts.map +1 -0
  214. package/dist/src/shaders/wgsl/hello.wgsl.d.ts +2 -0
  215. package/dist/src/shaders/wgsl/hello.wgsl.d.ts.map +1 -0
  216. package/dist/src/shaders/wgsl/helloInstanced.wgsl.d.ts +2 -0
  217. package/dist/src/shaders/wgsl/helloInstanced.wgsl.d.ts.map +1 -0
  218. package/dist/src/shaders/wgsl/quad.wgsl.d.ts +2 -0
  219. package/dist/src/shaders/wgsl/quad.wgsl.d.ts.map +1 -0
  220. package/dist/src/text/FontPipeline.d.ts +13 -0
  221. package/dist/src/text/FontPipeline.d.ts.map +1 -0
  222. package/dist/src/text/MsdfFont.d.ts +81 -0
  223. package/dist/src/text/MsdfFont.d.ts.map +1 -0
  224. package/dist/src/text/TextFormatting.d.ts +18 -0
  225. package/dist/src/text/TextFormatting.d.ts.map +1 -0
  226. package/dist/src/text/TextNode.d.ts +18 -0
  227. package/dist/src/text/TextNode.d.ts.map +1 -0
  228. package/dist/src/text/TextShader.d.ts +14 -0
  229. package/dist/src/text/TextShader.d.ts.map +1 -0
  230. package/dist/src/text/mod.d.ts +3 -0
  231. package/dist/src/text/mod.d.ts.map +1 -0
  232. package/dist/src/text/shaping.d.ts +38 -0
  233. package/dist/src/text/shaping.d.ts.map +1 -0
  234. package/dist/src/text/text.wgsl.d.ts +2 -0
  235. package/dist/src/text/text.wgsl.d.ts.map +1 -0
  236. package/dist/src/textures/AssetManager.d.ts +181 -0
  237. package/dist/src/textures/AssetManager.d.ts.map +1 -0
  238. package/dist/src/textures/NewTextureComputeShader.d.ts +28 -0
  239. package/dist/src/textures/TextureComputeShader.d.ts +20 -0
  240. package/dist/src/textures/TextureComputeShader.d.ts.map +1 -0
  241. package/dist/src/textures/crop.wgsl.d.ts +2 -0
  242. package/dist/src/textures/mod.d.ts +1 -0
  243. package/dist/src/textures/mod.d.ts.map +1 -0
  244. package/dist/src/textures/pixel-scraping.wgsl.d.ts +2 -0
  245. package/dist/src/textures/pixel-scraping.wgsl.d.ts.map +1 -0
  246. package/dist/src/textures/texture-processing.wgsl.d.ts +2 -0
  247. package/dist/src/textures/types.d.ts +176 -0
  248. package/dist/src/textures/types.d.ts.map +1 -0
  249. package/dist/src/textures/util.d.ts +7 -0
  250. package/dist/src/textures/util.d.ts.map +1 -0
  251. package/dist/src/utils/assert.d.ts +1 -0
  252. package/dist/src/utils/assert.d.ts.map +1 -0
  253. package/dist/src/utils/boilerplate.d.ts +10 -0
  254. package/dist/src/utils/boilerplate.d.ts.map +1 -0
  255. package/dist/src/utils/error.d.ts +7 -0
  256. package/dist/src/utils/error.d.ts.map +1 -0
  257. package/dist/src/utils/mod.d.ts +2 -0
  258. package/dist/src/utils/mod.d.ts.map +1 -0
  259. package/dist/src/utils/pool.d.ts +22 -0
  260. package/dist/src/utils/pool.d.ts.map +1 -0
  261. package/dist/test/math/matrix.test.d.ts +1 -0
  262. package/dist/test/scene/Batcher.test.d.ts +1 -0
  263. package/dist/test/scene/SceneNode.test.d.ts +1 -0
  264. package/dist/test/shader/parser.test.d.ts +1 -0
  265. package/dist/text/FontPipeline.d.ts +14 -0
  266. package/dist/text/FontPipeline.d.ts.map +1 -0
  267. package/dist/text/MsdfFont.d.ts +82 -0
  268. package/dist/text/MsdfFont.d.ts.map +1 -0
  269. package/dist/text/TextFormatting.d.ts +19 -0
  270. package/dist/text/TextFormatting.d.ts.map +1 -0
  271. package/dist/text/TextNode.d.ts +19 -0
  272. package/dist/text/TextNode.d.ts.map +1 -0
  273. package/dist/text/TextShader.d.ts +15 -0
  274. package/dist/text/TextShader.d.ts.map +1 -0
  275. package/dist/text/mod.d.ts +4 -0
  276. package/dist/text/mod.d.ts.map +1 -0
  277. package/dist/text/shaping.d.ts +39 -0
  278. package/dist/text/shaping.d.ts.map +1 -0
  279. package/dist/text/text.wgsl.d.ts +3 -0
  280. package/dist/text/text.wgsl.d.ts.map +1 -0
  281. package/dist/textures/AssetManager.d.ts +182 -0
  282. package/dist/textures/AssetManager.d.ts.map +1 -0
  283. package/dist/textures/TextureComputeShader.d.ts +21 -0
  284. package/dist/textures/TextureComputeShader.d.ts.map +1 -0
  285. package/dist/textures/mod.d.ts +2 -0
  286. package/dist/textures/mod.d.ts.map +1 -0
  287. package/dist/textures/pixel-scraping.wgsl.d.ts +3 -0
  288. package/dist/textures/pixel-scraping.wgsl.d.ts.map +1 -0
  289. package/dist/textures/types.d.ts +177 -0
  290. package/dist/textures/types.d.ts.map +1 -0
  291. package/dist/textures/util.d.ts +8 -0
  292. package/dist/textures/util.d.ts.map +1 -0
  293. package/dist/utils/assert.d.ts +2 -0
  294. package/dist/utils/assert.d.ts.map +1 -0
  295. package/dist/utils/boilerplate.d.ts +11 -0
  296. package/dist/utils/boilerplate.d.ts.map +1 -0
  297. package/dist/utils/error.d.ts +8 -0
  298. package/dist/utils/error.d.ts.map +1 -0
  299. package/dist/utils/mod.d.ts +3 -0
  300. package/dist/utils/mod.d.ts.map +1 -0
  301. package/dist/utils/pool.d.ts +23 -0
  302. package/dist/utils/pool.d.ts.map +1 -0
  303. package/package.json +47 -0
  304. package/src/Toodle.ts +853 -0
  305. package/src/colors/mod.ts +151 -0
  306. package/src/coreTypes/Color.ts +1 -0
  307. package/src/coreTypes/Point.ts +7 -0
  308. package/src/coreTypes/Size.ts +4 -0
  309. package/src/coreTypes/Transform.ts +16 -0
  310. package/src/coreTypes/Vec2.ts +7 -0
  311. package/src/coreTypes/mod.ts +5 -0
  312. package/src/globals.d.ts +4 -0
  313. package/src/limits.ts +23 -0
  314. package/src/math/angle.ts +17 -0
  315. package/src/math/matrix.ts +99 -0
  316. package/src/math/mod.ts +2 -0
  317. package/src/mod.ts +22 -0
  318. package/src/scene/Batcher.ts +61 -0
  319. package/src/scene/Camera.ts +69 -0
  320. package/src/scene/JumboQuadNode.ts +219 -0
  321. package/src/scene/QuadNode.ts +403 -0
  322. package/src/scene/RenderComponent.ts +12 -0
  323. package/src/scene/SceneNode.ts +668 -0
  324. package/src/scene/mod.ts +4 -0
  325. package/src/screen/mod.ts +1 -0
  326. package/src/screen/resolution.ts +1 -0
  327. package/src/shaders/EngineUniform.ts +11 -0
  328. package/src/shaders/IShader.ts +20 -0
  329. package/src/shaders/QuadShader.ts +288 -0
  330. package/src/shaders/ShaderDescriptor.ts +6 -0
  331. package/src/shaders/mod.ts +5 -0
  332. package/src/shaders/parser.ts +221 -0
  333. package/src/shaders/postprocess/blur.ts +245 -0
  334. package/src/shaders/postprocess/mod.ts +71 -0
  335. package/src/shaders/samplers.ts +13 -0
  336. package/src/shaders/wgsl/example.wgsl.ts +24 -0
  337. package/src/shaders/wgsl/hello.wgsl.ts +62 -0
  338. package/src/shaders/wgsl/helloInstanced.wgsl.ts +46 -0
  339. package/src/shaders/wgsl/quad.wgsl.ts +140 -0
  340. package/src/text/FontPipeline.ts +212 -0
  341. package/src/text/MsdfFont.ts +190 -0
  342. package/src/text/TextFormatting.ts +28 -0
  343. package/src/text/TextNode.ts +82 -0
  344. package/src/text/TextShader.ts +223 -0
  345. package/src/text/mod.ts +8 -0
  346. package/src/text/shaping.ts +280 -0
  347. package/src/text/text.wgsl.ts +149 -0
  348. package/src/textures/AssetManager.ts +746 -0
  349. package/src/textures/TextureComputeShader.ts +434 -0
  350. package/src/textures/mod.ts +1 -0
  351. package/src/textures/pixel-scraping.wgsl.ts +131 -0
  352. package/src/textures/types.ts +182 -0
  353. package/src/textures/util.ts +352 -0
  354. package/src/utils/assert.ts +5 -0
  355. package/src/utils/boilerplate.ts +110 -0
  356. package/src/utils/error.ts +14 -0
  357. package/src/utils/mod.ts +2 -0
  358. package/src/utils/pool.ts +42 -0
@@ -0,0 +1,746 @@
1
+ import type { Size } from "../coreTypes/Size";
2
+ import type { Vec2 } from "../coreTypes/Vec2";
3
+ import type { Limits } from "../limits";
4
+ import { JumboQuadNode } from "../scene/JumboQuadNode";
5
+ import { QuadNode } from "../scene/QuadNode";
6
+ import type { SceneNode } from "../scene/SceneNode";
7
+ import { FontPipeline } from "../text/FontPipeline";
8
+ import { MsdfFont } from "../text/MsdfFont";
9
+ import { TextShader } from "../text/TextShader";
10
+ import { assert } from "../utils/mod";
11
+ import { TextureComputeShader } from "./TextureComputeShader";
12
+ import type {
13
+ AtlasBundleOpts,
14
+ AtlasCoords,
15
+ CpuTextureAtlas,
16
+ PixiRegion,
17
+ TextureBundleOpts,
18
+ TextureRegion,
19
+ TextureWithMetadata,
20
+ } from "./types";
21
+ import { getBitmapFromUrl, packBitmapsToAtlas } from "./util";
22
+
23
+ export type TextureId = string;
24
+ export type BundleId = string;
25
+ export type FontId = string;
26
+
27
+ type Bundle = {
28
+ atlases: CpuTextureAtlas[];
29
+ isLoaded: boolean;
30
+ atlasIndices: number[];
31
+ };
32
+
33
+ export class AssetManager {
34
+ readonly textureAtlas: GPUTexture;
35
+ #device: GPUDevice;
36
+ #presentationFormat: GPUTextureFormat;
37
+ #bundles: Map<BundleId, Bundle> = new Map();
38
+ #textures: Map<string, AtlasCoords[]> = new Map();
39
+ #fonts: Map<string, TextShader> = new Map();
40
+ #cropComputeShader: TextureComputeShader;
41
+ #limits: Limits;
42
+ #availableIndices: Set<number> = new Set();
43
+
44
+ constructor(
45
+ device: GPUDevice,
46
+ presentationFormat: GPUTextureFormat,
47
+ limits: Limits,
48
+ format: "rgba8unorm" | "rg8unorm" = "rgba8unorm",
49
+ ) {
50
+ this.#device = device;
51
+ this.#presentationFormat = presentationFormat;
52
+ this.#limits = limits;
53
+ this.textureAtlas = device.createTexture({
54
+ label: "Asset Manager Atlas Texture",
55
+ size: [
56
+ this.#limits.textureSize,
57
+ this.#limits.textureSize,
58
+ this.#limits.textureArrayLayers,
59
+ ],
60
+ format,
61
+ usage:
62
+ GPUTextureUsage.TEXTURE_BINDING |
63
+ GPUTextureUsage.COPY_DST |
64
+ GPUTextureUsage.RENDER_ATTACHMENT,
65
+ });
66
+ this.#cropComputeShader = TextureComputeShader.create(device);
67
+ this.#availableIndices = new Set(
68
+ Array.from({ length: limits.textureArrayLayers }, (_, i) => i),
69
+ );
70
+ }
71
+
72
+ /**
73
+ * True dimensions of a loaded texture, prior to any transparent pixel cropping.
74
+ *
75
+ * @param id - The id of the texture to get the size of
76
+ * @returns The size of the texture
77
+ */
78
+ getSize(id: TextureId): Size {
79
+ const coords = this.extra.getAtlasCoords(id);
80
+ const originalScale = coords[0].uvScale;
81
+ return {
82
+ width: originalScale.width * this.textureAtlas.width,
83
+ height: originalScale.height * this.textureAtlas.height,
84
+ };
85
+ }
86
+
87
+ /**
88
+ * Dimensions of a loaded texture, cropped to a minimal bounding box.
89
+ *
90
+ * @param id - The id of the texture to get the size of
91
+ * @returns The size of the texture
92
+ */
93
+ getCroppedSize(id: TextureId): Size {
94
+ const scaledUvs = this.extra.getAtlasCoords(id)[0].uvScaleCropped;
95
+ if (scaledUvs) {
96
+ return {
97
+ width: scaledUvs.width * this.textureAtlas.width,
98
+ height: scaledUvs.height * this.textureAtlas.height,
99
+ };
100
+ }
101
+ return this.getSize(id);
102
+ }
103
+
104
+ /**
105
+ * Whether the texture has been cropped for extra transparency.
106
+ *
107
+ * @param id - The id of the texture to be checked
108
+ * @returns Whether the image has been cropped (i.e. if it has uvScaledCropped)
109
+ */
110
+ isCropped(id: TextureId): boolean {
111
+ if (!this.#textures.has(id)) {
112
+ throw new Error(
113
+ `Texture ${id} not found in atlas. Have you called toodle.loadTextures with this id or toodle.loadBundle with a bundle that contains it?`,
114
+ );
115
+ }
116
+
117
+ return this.#textures.get(id)![0].uvScaleCropped === undefined;
118
+ }
119
+
120
+ /**
121
+ * A read-only map of all currently loaded textures.
122
+ */
123
+ get textures() {
124
+ return this.#textures;
125
+ }
126
+
127
+ /**
128
+ * A read-only array of all currently loaded texture ids.
129
+ */
130
+ get textureIds() {
131
+ return Array.from(this.#textures.keys());
132
+ }
133
+
134
+ /**
135
+ * Debug method to load a set of textures from a record of URLS.
136
+ *
137
+ * @param textures Collection of strings and URLs representing the texture name and target file
138
+ * @param options LoadingOptions used to modify the loading process
139
+ *
140
+ * Note: this will consume one texture atlas per texture.
141
+ * For more efficient loading of multiple textures, consider {@link loadBundle}
142
+ *
143
+ * @example
144
+ *
145
+ * await toodle.assets.loadTextures({
146
+ * "myImage": new URL("assets/image.png", "https://mywebsite.com"),
147
+ * });
148
+ *
149
+ * @deprecated use {@link registerBundle} instead. or {@link loadTexture} for debugging
150
+ */
151
+ async loadTextures(opts: TextureBundleOpts["textures"]): Promise<void> {
152
+ await Promise.all(
153
+ Object.entries(opts).map(([id, url]) => this.loadTexture(id, url, opts)),
154
+ );
155
+ }
156
+
157
+ /**
158
+ * Debug method to load a single texture.
159
+ *
160
+ * @param id ID used to name the texture
161
+ * @param url URL or ImageBitmap target for the image
162
+ * @param options LoadingOptions used to modify the loading process
163
+ *
164
+ * Note: this will consume one texture atlas per texture.
165
+ * For more efficient loading of multiple textures, consider {@link loadBundle}
166
+ */
167
+ async loadTexture(
168
+ id: TextureId,
169
+ url: URL | ImageBitmap,
170
+ options?: Partial<TextureBundleOpts>,
171
+ ) {
172
+ const bitmap =
173
+ url instanceof ImageBitmap ? url : await getBitmapFromUrl(url);
174
+
175
+ let textureWrapper: TextureWithMetadata = this.#wrapBitmapToTexture(
176
+ bitmap,
177
+ id,
178
+ );
179
+ const atlasIndex = this.extra.nextAvailableAtlasIndex();
180
+
181
+ if (options?.cropTransparentPixels) {
182
+ textureWrapper =
183
+ await this.#cropComputeShader.processTexture(textureWrapper);
184
+ }
185
+
186
+ this.#copyTextureToAtlas(textureWrapper.texture, atlasIndex);
187
+
188
+ const coords: AtlasCoords = {
189
+ uvOffset: { x: 0, y: 0 },
190
+ cropOffset: textureWrapper.cropOffset,
191
+ uvScale: {
192
+ width: textureWrapper.texture.width / this.textureAtlas.width,
193
+ height: textureWrapper.texture.height / this.textureAtlas.height,
194
+ },
195
+ originalSize: textureWrapper.originalSize,
196
+ uvScaleCropped: {
197
+ width: textureWrapper.texture.width / this.textureAtlas.width,
198
+ height: textureWrapper.texture.height / this.textureAtlas.height,
199
+ },
200
+ atlasIndex,
201
+ };
202
+
203
+ this.#textures.set(id, [coords]);
204
+ this.#availableIndices.delete(atlasIndex);
205
+
206
+ textureWrapper.texture.destroy();
207
+ return { id, coords };
208
+ }
209
+
210
+ /**
211
+ * Register a bundle of textures.
212
+ *
213
+ * @param bundleId ID used to name the bundle
214
+ * @param bundle Collection of strings and URLs representing the texture name and target file
215
+ * @param options LoadingOptions used to modify the loading process
216
+ *
217
+ * See: https://toodle.gg/f849595b3ed13fc956fc1459a5cb5f0228f9d259/examples/texture-bundles.html
218
+ */
219
+ async registerBundle(
220
+ bundleId: BundleId,
221
+ opts: TextureBundleOpts | AtlasBundleOpts,
222
+ ): Promise<BundleId> {
223
+ if ("textures" in opts) {
224
+ await this.#registerBundleFromTextures(bundleId, opts);
225
+ } else {
226
+ await this.#registerBundleFromAtlases(bundleId, opts);
227
+ }
228
+
229
+ if (opts.autoLoad) {
230
+ await this.loadBundle(bundleId);
231
+ }
232
+ return bundleId;
233
+ }
234
+
235
+ /**
236
+ * Load a bundle of textures to the gpu
237
+ *
238
+ * See: https://toodle.gg/f849595b3ed13fc956fc1459a5cb5f0228f9d259/examples/texture-bundles.html
239
+ */
240
+ async loadBundle(bundleId: BundleId) {
241
+ const bundle = this.#bundles.get(bundleId);
242
+ if (!bundle) {
243
+ throw new Error(`Bundle ${bundleId} not found`);
244
+ }
245
+
246
+ if (bundle.isLoaded) {
247
+ console.warn(`Bundle ${bundleId} is already loaded.`);
248
+ return;
249
+ }
250
+
251
+ for (const atlas of bundle.atlases) {
252
+ const atlasIndex = await this.extra.loadAtlas(atlas);
253
+ bundle.atlasIndices.push(atlasIndex);
254
+ }
255
+
256
+ bundle.isLoaded = true;
257
+ }
258
+
259
+ /**
260
+ * Unload a bundle of textures from the gpu - this marks the gpu-side texture atlas
261
+ * as available for future texture loading.
262
+ *
263
+ * @param bundleId - The id of the bundle to unload
264
+ */
265
+ async unloadBundle(bundleId: BundleId) {
266
+ const bundle = this.#bundles.get(bundleId);
267
+ if (!bundle) {
268
+ throw new Error(`Bundle ${bundleId} not found`);
269
+ }
270
+
271
+ if (!bundle.isLoaded) {
272
+ console.warn(`Bundle ${bundleId} is not loaded.`);
273
+ return;
274
+ }
275
+
276
+ await Promise.all(
277
+ bundle.atlasIndices.map((atlasIndex) =>
278
+ this.extra.unloadAtlas(atlasIndex),
279
+ ),
280
+ );
281
+
282
+ bundle.isLoaded = false;
283
+ bundle.atlasIndices = [];
284
+ }
285
+
286
+ /**
287
+ * Load a font to the gpu
288
+ *
289
+ * @param id - The id of the font to load
290
+ * @param url - The url of the font to load
291
+ * @param fallbackCharacter - The character to use as a fallback if the font does not contain a character to be rendererd
292
+ */
293
+ async loadFont(id: string, url: URL, fallbackCharacter = "_") {
294
+ const font = await MsdfFont.create(id, url);
295
+ font.fallbackCharacter = fallbackCharacter;
296
+ const fontPipeline = await FontPipeline.create(
297
+ this.#device,
298
+ font,
299
+ this.#presentationFormat,
300
+ this.#limits.maxTextLength,
301
+ );
302
+
303
+ const textShader = new TextShader(
304
+ this.#device,
305
+ fontPipeline,
306
+ font,
307
+ this.#presentationFormat,
308
+ this.#limits.instanceCount,
309
+ );
310
+ this.#fonts.set(id, textShader);
311
+ return id;
312
+ }
313
+
314
+ getFont(id: string) {
315
+ if (!this.#fonts.has(id)) {
316
+ throw new Error(
317
+ `Font ${id} not found in atlas. Have you called toodle.loadFont with this id?`,
318
+ );
319
+ }
320
+ return this.#fonts.get(id)!;
321
+ }
322
+
323
+ validateTextureReference(node: SceneNode | QuadNode) {
324
+ if (
325
+ !(node instanceof QuadNode) ||
326
+ node.isPrimitive ||
327
+ node instanceof JumboQuadNode
328
+ )
329
+ return;
330
+
331
+ const coords: AtlasCoords[] | undefined = this.#textures.get(
332
+ node.textureId,
333
+ );
334
+ if (!coords || !coords.length) {
335
+ throw new Error(
336
+ `Node ${node.id} references an invalid texture ${node.textureId}.`,
337
+ );
338
+ }
339
+
340
+ if (
341
+ coords.find((coord) => coord.atlasIndex === node.atlasCoords.atlasIndex)
342
+ )
343
+ return;
344
+
345
+ node.extra.setAtlasCoords(coords[0]);
346
+ }
347
+
348
+ /**
349
+ * Sets a designated texture ID to the corresponding `AtlasRegion` built from a `TextureRegion` and `numerical atlas index.
350
+ * @param id - `String` representing the texture name. I.e. "PlayerSprite"
351
+ * @param textureRegion - `TextureRegion` corresponding the uv and texture offsets
352
+ * @param atlasIndex - `number` of the atlas that the texture will live in.
353
+ * @private
354
+ */
355
+ #addTexture(id: string, textureRegion: TextureRegion, atlasIndex: number) {
356
+ this.#textures.set(id, [
357
+ {
358
+ ...textureRegion,
359
+ atlasIndex,
360
+ },
361
+ ]);
362
+ }
363
+
364
+ /**
365
+ *
366
+ * @param bitmap - `ImageBitmap` to be processed into a `GPUTexture` for storage and manipulation
367
+ * @param name - Used to name the new `GPUTexture` via labeling.
368
+ * @private
369
+ */
370
+ #createTextureFromImageBitmap(bitmap: ImageBitmap, name: string): GPUTexture {
371
+ const texture = this.#device.createTexture({
372
+ label: `${name} Intermediary Texture`,
373
+ size: [bitmap.width, bitmap.height],
374
+ format: "rgba8unorm",
375
+ usage:
376
+ GPUTextureUsage.COPY_DST |
377
+ GPUTextureUsage.COPY_SRC |
378
+ GPUTextureUsage.TEXTURE_BINDING |
379
+ GPUTextureUsage.RENDER_ATTACHMENT,
380
+ });
381
+
382
+ this.#device.queue.copyExternalImageToTexture(
383
+ {
384
+ source: bitmap,
385
+ },
386
+ {
387
+ texture,
388
+ },
389
+ [bitmap.width, bitmap.height],
390
+ );
391
+ return texture;
392
+ }
393
+
394
+ async #registerBundleFromTextures(
395
+ bundleId: BundleId,
396
+ opts: TextureBundleOpts,
397
+ ) {
398
+ const images = new Map<string, TextureWithMetadata>();
399
+
400
+ let networkLoadTime = 0;
401
+ await Promise.all(
402
+ Object.entries(opts.textures).map(async ([id, url]) => {
403
+ const now = performance.now();
404
+ const bitmap = await getBitmapFromUrl(url);
405
+ networkLoadTime += performance.now() - now;
406
+ let textureWrapper: TextureWithMetadata = this.#wrapBitmapToTexture(
407
+ bitmap,
408
+ id,
409
+ );
410
+
411
+ if (opts.cropTransparentPixels) {
412
+ textureWrapper =
413
+ await this.#cropComputeShader.processTexture(textureWrapper);
414
+ }
415
+ images.set(id, textureWrapper);
416
+ }),
417
+ );
418
+
419
+ const atlases = await packBitmapsToAtlas(
420
+ images,
421
+ this.#limits.textureSize,
422
+ this.#device,
423
+ );
424
+
425
+ this.#bundles.set(bundleId, {
426
+ atlases,
427
+ atlasIndices: [],
428
+ isLoaded: false,
429
+ });
430
+ }
431
+
432
+ async #registerBundleFromAtlases(bundleId: BundleId, opts: AtlasBundleOpts) {
433
+ const atlases: CpuTextureAtlas[] = [];
434
+
435
+ for (const atlas of opts.atlases) {
436
+ const jsonUrl =
437
+ atlas.json ??
438
+ new URL(
439
+ atlas.png!.toString().replace(".png", ".json"),
440
+ atlas.png!.origin,
441
+ );
442
+ const pngUrl =
443
+ atlas.png ??
444
+ new URL(
445
+ atlas.json!.toString().replace(".json", ".png"),
446
+ atlas.json!.origin,
447
+ );
448
+
449
+ const atlasDef = await (await fetch(jsonUrl)).json();
450
+ const bitmap = !opts.rg8
451
+ ? await getBitmapFromUrl(pngUrl)
452
+ : await createImageBitmap(new ImageData(1, 1)); // placeholder bitmap if using rg8
453
+
454
+ let rg8Bytes: Uint8Array<ArrayBuffer> | undefined;
455
+ if (opts.rg8) {
456
+ const rg8url = new URL(
457
+ pngUrl.toString().replace(".png", ".rg8.gz"),
458
+ pngUrl.origin,
459
+ );
460
+ const rgBytes = await fetch(rg8url).then(async (r) => {
461
+ const enc = (r.headers.get("content-encoding") || "").toLowerCase();
462
+ // If server/CDN already set Content-Encoding, Fetch returns decompressed bytes.
463
+ if (
464
+ enc.includes("gzip") ||
465
+ enc.includes("br") ||
466
+ enc.includes("deflate")
467
+ ) {
468
+ return new Uint8Array(await r.arrayBuffer());
469
+ }
470
+
471
+ assert(r.body, "Response body of rg8 file is null");
472
+ const ds = new DecompressionStream("gzip");
473
+ const ab = await new Response(r.body.pipeThrough(ds)).arrayBuffer();
474
+ return new Uint8Array(ab);
475
+ });
476
+ rg8Bytes = rgBytes;
477
+ }
478
+
479
+ const cpuTextureAtlas: CpuTextureAtlas = {
480
+ texture: bitmap,
481
+ rg8Bytes,
482
+ textureRegions: new Map(),
483
+ width: opts.rg8 ? this.#limits.textureSize : bitmap.width,
484
+ height: opts.rg8 ? this.#limits.textureSize : bitmap.height,
485
+ };
486
+
487
+ for (const [assetId, frame] of Object.entries(atlasDef.frames) as [
488
+ string,
489
+ PixiRegion,
490
+ ][]) {
491
+ const leftCrop = frame.spriteSourceSize.x;
492
+ const rightCrop =
493
+ frame.sourceSize.w -
494
+ frame.spriteSourceSize.x -
495
+ frame.spriteSourceSize.w;
496
+ const topCrop = frame.spriteSourceSize.y;
497
+ const bottomCrop =
498
+ frame.sourceSize.h -
499
+ frame.spriteSourceSize.y -
500
+ frame.spriteSourceSize.h;
501
+
502
+ cpuTextureAtlas.textureRegions.set(assetId, {
503
+ cropOffset: {
504
+ x: leftCrop - rightCrop,
505
+ y: bottomCrop - topCrop,
506
+ },
507
+ originalSize: {
508
+ width: frame.sourceSize.w,
509
+ height: frame.sourceSize.h,
510
+ },
511
+ uvOffset: {
512
+ x: frame.frame.x / cpuTextureAtlas.width,
513
+ y: frame.frame.y / cpuTextureAtlas.height,
514
+ },
515
+ uvScale: {
516
+ width: frame.sourceSize.w / cpuTextureAtlas.width,
517
+ height: frame.sourceSize.h / cpuTextureAtlas.height,
518
+ },
519
+ uvScaleCropped: {
520
+ width: frame.frame.w / cpuTextureAtlas.width,
521
+ height: frame.frame.h / cpuTextureAtlas.height,
522
+ },
523
+ });
524
+ }
525
+
526
+ atlases.push(cpuTextureAtlas);
527
+ }
528
+
529
+ this.#bundles.set(bundleId, {
530
+ atlases,
531
+ atlasIndices: [],
532
+ isLoaded: false,
533
+ });
534
+ }
535
+
536
+ /**
537
+ * Advanced and niche features
538
+ */
539
+ extra = {
540
+ // Get an array of all currently registered bundle ids.
541
+ getRegisteredBundleIds: (): string[] => {
542
+ return this.#bundles ? Array.from(this.#bundles.keys()) : [];
543
+ },
544
+
545
+ // Get an array of all currently loaded bundle ids.
546
+ getLoadedBundleIds: (): string[] => {
547
+ return Array.from(this.#bundles.entries())
548
+ .filter(([, value]) => value.isLoaded)
549
+ .map(([key]) => key);
550
+ },
551
+
552
+ /**
553
+ * Set the atlas coordinates for a texture.
554
+ *
555
+ * This should not be necessary for most use cases. This allows for UV precision
556
+ *
557
+ * @param id - The id of the texture to set the atlas coordinates for
558
+ * @param coords - The atlas coordinates to set
559
+ */
560
+ setAtlasCoords: (id: TextureId, coords: AtlasCoords) => {
561
+ const oldCoords: AtlasCoords[] | undefined = this.#textures.get(id);
562
+ if (!oldCoords) return;
563
+ const indexToModify = oldCoords.findIndex(
564
+ (coord) => coord.atlasIndex === coords.atlasIndex,
565
+ );
566
+ if (indexToModify === -1) return;
567
+ oldCoords[indexToModify] = coords;
568
+ this.#textures.set(id, oldCoords);
569
+ },
570
+
571
+ /**
572
+ * Get the atlas coordinates for a texture.
573
+ *
574
+ * @param id - The id of the texture to get the atlas coordinates for
575
+ * @returns An array of the atlas coordinates for the texture
576
+ */
577
+ getAtlasCoords: (id: TextureId): AtlasCoords[] => {
578
+ if (!this.#textures.has(id)) {
579
+ throw new Error(
580
+ `Texture ${id} not found in atlas. Have you called toodle.loadBundle with a bundle that contains this id (or toodle.loadTextures with this id as a key)?`,
581
+ );
582
+ }
583
+ return this.#textures.get(id) ?? [];
584
+ },
585
+
586
+ /**
587
+ * Get the texture default offset
588
+ *
589
+ * @param id - The id of the texture to get the atlas coordinates for
590
+ * @returns Point of the texture's associated X,Y offset
591
+ */
592
+ getTextureOffset: (id: TextureId): Vec2 => {
593
+ const texture: AtlasCoords[] | undefined = this.#textures.get(id);
594
+ if (!texture) {
595
+ throw new Error(
596
+ `Texture ${id} not found in atlas. Have you called toodle.loadTextures with this id or toodle.loadBundle with a bundle that contains it?`,
597
+ );
598
+ }
599
+ return texture[0].cropOffset;
600
+ },
601
+
602
+ /**
603
+ * Get diagnostics on texture atlas usage
604
+ *
605
+ * @returns Usage stats for texture atlases
606
+ */
607
+ getAtlasUsage: () => {
608
+ return {
609
+ /**
610
+ * The number of texture atlases that are currently unused
611
+ * and available to load textures into.
612
+ */
613
+ available: this.#availableIndices.size,
614
+ /**
615
+ * The number of texture atlases that are currently in use.
616
+ */
617
+ used: this.#limits.textureArrayLayers - this.#availableIndices.size,
618
+ /**
619
+ * The total number of texture atlases that can be loaded.
620
+ */
621
+ total: this.#limits.textureArrayLayers,
622
+ };
623
+ },
624
+
625
+ /**
626
+ * Consume the next available atlas index.
627
+ *
628
+ */
629
+ nextAvailableAtlasIndex: () => {
630
+ for (let i = 0; i < this.#limits.textureArrayLayers; i++) {
631
+ if (this.#availableIndices.has(i)) {
632
+ this.#availableIndices.delete(i);
633
+ return i;
634
+ }
635
+ }
636
+ throw new Error("Texture atlas is full - too many textures loaded.");
637
+ },
638
+
639
+ /**
640
+ * Load a texture atlas from a CpuTextureAtlas.
641
+ *
642
+ * @param atlas - The texture atlas to load
643
+ * @returns The index of the atlas
644
+ */
645
+ loadAtlas: async (atlas: CpuTextureAtlas) => {
646
+ const atlasIndex = this.extra.nextAvailableAtlasIndex();
647
+
648
+ if (atlas.rg8Bytes) {
649
+ const { width: w, height: h } = {
650
+ width: this.textureAtlas.width,
651
+ height: this.textureAtlas.height,
652
+ };
653
+
654
+ // WebGPU requires 256-byte bytesPerRow
655
+ const rowBytes = w * 2;
656
+ assert(rowBytes % 256 === 0, "rowBytes must be a multiple of 256");
657
+
658
+ this.#device.queue.writeTexture(
659
+ { texture: this.textureAtlas, origin: { x: 0, y: 0, z: atlasIndex } },
660
+ atlas.rg8Bytes,
661
+ { bytesPerRow: rowBytes, rowsPerImage: h },
662
+ { width: w, height: h, depthOrArrayLayers: 1 },
663
+ );
664
+ } else {
665
+ this.#device.queue.copyExternalImageToTexture(
666
+ {
667
+ source: atlas.texture,
668
+ },
669
+ {
670
+ texture: this.textureAtlas,
671
+ origin: [0, 0, atlasIndex],
672
+ },
673
+ [atlas.texture.width, atlas.texture.height, 1],
674
+ );
675
+ }
676
+
677
+ for (const [id, region] of atlas.textureRegions) {
678
+ const existing = this.#textures.get(id);
679
+ if (existing) {
680
+ existing.push({ ...region, atlasIndex });
681
+ } else this.#addTexture(id, region, atlasIndex);
682
+ }
683
+ return atlasIndex;
684
+ },
685
+
686
+ /**
687
+ * Unload an atlas from the texture atlas.
688
+ *
689
+ * @param atlasIndex - The index of the atlas to unload
690
+ */
691
+ unloadAtlas: async (atlasIndex: number) => {
692
+ this.#availableIndices.add(atlasIndex);
693
+ for (const [id, coords] of this.#textures.entries()) {
694
+ const indexToModify = coords.findIndex(
695
+ (coord) => coord.atlasIndex === atlasIndex,
696
+ );
697
+ if (indexToModify !== -1) {
698
+ coords.splice(indexToModify, 1);
699
+ }
700
+ if (!coords.length) {
701
+ this.#textures.delete(id);
702
+ }
703
+ }
704
+ },
705
+ };
706
+
707
+ #wrapBitmapToTexture(
708
+ bitmap: ImageBitmap,
709
+ name = "Unknown",
710
+ ): TextureWithMetadata {
711
+ const texture: GPUTexture = this.#createTextureFromImageBitmap(
712
+ bitmap,
713
+ name,
714
+ );
715
+ return {
716
+ texture,
717
+ cropOffset: { x: 0, y: 0 },
718
+ originalSize: { width: texture.width, height: texture.height },
719
+ };
720
+ }
721
+
722
+ #copyTextureToAtlas(texture: GPUTexture, atlasIndex: number) {
723
+ const copyEncoder: GPUCommandEncoder = this.#device.createCommandEncoder();
724
+ copyEncoder.copyTextureToTexture(
725
+ {
726
+ texture: texture,
727
+ mipLevel: 0,
728
+ origin: [0, 0, 0],
729
+ },
730
+ {
731
+ texture: this.textureAtlas,
732
+ mipLevel: 0,
733
+ origin: [0, 0, atlasIndex],
734
+ },
735
+ [texture.width, texture.height, 1],
736
+ );
737
+
738
+ this.#device.queue.submit([copyEncoder.finish()]);
739
+ }
740
+ /**
741
+ * Destroy the texture atlas. This should free up ~4gb of gpu memory (and make all draw calls fail)
742
+ */
743
+ destroy() {
744
+ this.textureAtlas.destroy();
745
+ }
746
+ }