@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.
- package/LICENSE +21 -0
- package/README.md +44 -0
- package/dist/Toodle.d.ts +304 -0
- package/dist/Toodle.d.ts.map +1 -0
- package/dist/colors/mod.d.ts +872 -0
- package/dist/colors/mod.d.ts.map +1 -0
- package/dist/coreTypes/Color.d.ts +7 -0
- package/dist/coreTypes/Color.d.ts.map +1 -0
- package/dist/coreTypes/Point.d.ts +8 -0
- package/dist/coreTypes/Point.d.ts.map +1 -0
- package/dist/coreTypes/Size.d.ts +5 -0
- package/dist/coreTypes/Size.d.ts.map +1 -0
- package/dist/coreTypes/Transform.d.ts +16 -0
- package/dist/coreTypes/Transform.d.ts.map +1 -0
- package/dist/coreTypes/Vec2.d.ts +8 -0
- package/dist/coreTypes/Vec2.d.ts.map +1 -0
- package/dist/coreTypes/mod.d.ts +6 -0
- package/dist/coreTypes/mod.d.ts.map +1 -0
- package/dist/docs/snippets/add-remove-children.d.ts +1 -0
- package/dist/docs/snippets/basic-quad.d.ts +1 -0
- package/dist/docs/snippets/filter-linear.d.ts +1 -0
- package/dist/docs/snippets/filter-nearest.d.ts +1 -0
- package/dist/docs/snippets/flipxy.d.ts +1 -0
- package/dist/docs/snippets/hello-text.d.ts +1 -0
- package/dist/docs/snippets/jumbo-textures.d.ts +1 -0
- package/dist/docs/snippets/layer.d.ts +1 -0
- package/dist/docs/snippets/layout-edges.d.ts +1 -0
- package/dist/docs/snippets/layout-screen-and-world-space.d.ts +1 -0
- package/dist/docs/snippets/postprocess.d.ts +1 -0
- package/dist/docs/snippets/quad-size-scale.d.ts +1 -0
- package/dist/docs/snippets/quickstart.d.ts +1 -0
- package/dist/docs/snippets/repeat-texture-loading.d.ts +1 -0
- package/dist/docs/snippets/screen-shaders.d.ts +1 -0
- package/dist/docs/snippets/shader-color-flash.d.ts +1 -0
- package/dist/docs/snippets/shader-default.d.ts +1 -0
- package/dist/docs/snippets/shader-fill.d.ts +1 -0
- package/dist/docs/snippets/shapes-line.d.ts +1 -0
- package/dist/docs/snippets/sprite-region.d.ts +1 -0
- package/dist/docs/snippets/text-alignment.d.ts +1 -0
- package/dist/docs/snippets/text-shrink-to-fit.d.ts +1 -0
- package/dist/docs/snippets/text-word-wrap.d.ts +1 -0
- package/dist/docs/snippets/texture-bundles-prebaked.d.ts +1 -0
- package/dist/docs/snippets/texture-bundles.d.ts +1 -0
- package/dist/docs/snippets/transforms.d.ts +1 -0
- package/dist/docs/snippets/transparent-cropping.d.ts +1 -0
- package/dist/examples/0-hello.d.ts +1 -0
- package/dist/examples/1-hello.d.ts +1 -0
- package/dist/examples/1-hello.d.ts.map +1 -0
- package/dist/examples/1-quad.d.ts +1 -0
- package/dist/examples/10-resize.d.ts +1 -0
- package/dist/examples/10-resize.d.ts.map +1 -0
- package/dist/examples/11-bundle-test.d.ts +1 -0
- package/dist/examples/11-bundle-test.d.ts.map +1 -0
- package/dist/examples/12-transparent-pixel-cropping.d.ts +1 -0
- package/dist/examples/12-transparent-pixel-cropping.d.ts.map +1 -0
- package/dist/examples/13-crop.d.ts +1 -0
- package/dist/examples/13-crop.d.ts.map +1 -0
- package/dist/examples/14-bundle-bench.d.ts +1 -0
- package/dist/examples/14-bundle-bench.d.ts.map +1 -0
- package/dist/examples/15-text-layer.d.ts +1 -0
- package/dist/examples/15-text-layer.d.ts.map +1 -0
- package/dist/examples/16-jumbo-texture.d.ts +1 -0
- package/dist/examples/16-jumbo-textures.d.ts +1 -0
- package/dist/examples/16-screen-shader.d.ts +1 -0
- package/dist/examples/16-screen-shader.d.ts.map +1 -0
- package/dist/examples/17-lighting.d.ts +1 -0
- package/dist/examples/17-lighting.d.ts.map +1 -0
- package/dist/examples/17-translations.d.ts +1 -0
- package/dist/examples/18-blur.d.ts +1 -0
- package/dist/examples/19-postprocess.d.ts +1 -0
- package/dist/examples/19-screenshader.d.ts +1 -0
- package/dist/examples/2-shapes.d.ts +1 -0
- package/dist/examples/2-shapes.d.ts.map +1 -0
- package/dist/examples/3-shader.d.ts +1 -0
- package/dist/examples/3-shader.d.ts.map +1 -0
- package/dist/examples/4-shader-bench.d.ts +1 -0
- package/dist/examples/4-shader-bench.d.ts.map +1 -0
- package/dist/examples/5-z.d.ts +1 -0
- package/dist/examples/5-z.d.ts.map +1 -0
- package/dist/examples/6-atlas.d.ts +1 -0
- package/dist/examples/6-atlas.d.ts.map +1 -0
- package/dist/examples/7-text.d.ts +1 -0
- package/dist/examples/7-text.d.ts.map +1 -0
- package/dist/examples/8-text-bench.d.ts +1 -0
- package/dist/examples/8-text-bench.d.ts.map +1 -0
- package/dist/examples/9-alignment.d.ts +1 -0
- package/dist/examples/9-alignment.d.ts.map +1 -0
- package/dist/examples/main.d.ts +1 -0
- package/dist/examples/main.d.ts.map +1 -0
- package/dist/examples/util.d.ts +82 -0
- package/dist/examples/util.d.ts.map +1 -0
- package/dist/limits.d.ts +23 -0
- package/dist/limits.d.ts.map +1 -0
- package/dist/math/angle.d.ts +13 -0
- package/dist/math/angle.d.ts.map +1 -0
- package/dist/math/matrix.d.ts +26 -0
- package/dist/math/matrix.d.ts.map +1 -0
- package/dist/math/mod.d.ts +3 -0
- package/dist/math/mod.d.ts.map +1 -0
- package/dist/mod.d.ts +17 -0
- package/dist/mod.d.ts.map +1 -0
- package/dist/mod.js +19665 -0
- package/dist/mod.js.map +41 -0
- package/dist/postprocess.d.ts +10 -0
- package/dist/postprocess.d.ts.map +1 -0
- package/dist/scene/Batcher.d.ts +20 -0
- package/dist/scene/Batcher.d.ts.map +1 -0
- package/dist/scene/Camera.d.ts +16 -0
- package/dist/scene/Camera.d.ts.map +1 -0
- package/dist/scene/JumboQuadNode.d.ts +29 -0
- package/dist/scene/JumboQuadNode.d.ts.map +1 -0
- package/dist/scene/QuadNode.d.ts +159 -0
- package/dist/scene/QuadNode.d.ts.map +1 -0
- package/dist/scene/RenderComponent.d.ts +11 -0
- package/dist/scene/RenderComponent.d.ts.map +1 -0
- package/dist/scene/SceneNode.d.ts +300 -0
- package/dist/scene/SceneNode.d.ts.map +1 -0
- package/dist/scene/mod.d.ts +5 -0
- package/dist/scene/mod.d.ts.map +1 -0
- package/dist/screen/mod.d.ts +2 -0
- package/dist/screen/mod.d.ts.map +1 -0
- package/dist/screen/resolution.d.ts +5 -0
- package/dist/screen/resolution.d.ts.map +1 -0
- package/dist/shaders/EngineUniform.d.ts +9 -0
- package/dist/shaders/EngineUniform.d.ts.map +1 -0
- package/dist/shaders/IShader.d.ts +15 -0
- package/dist/shaders/IShader.d.ts.map +1 -0
- package/dist/shaders/QuadShader.d.ts +18 -0
- package/dist/shaders/QuadShader.d.ts.map +1 -0
- package/dist/shaders/ShaderDescriptor.d.ts +7 -0
- package/dist/shaders/ShaderDescriptor.d.ts.map +1 -0
- package/dist/shaders/mod.d.ts +6 -0
- package/dist/shaders/mod.d.ts.map +1 -0
- package/dist/shaders/parser.d.ts +8 -0
- package/dist/shaders/parser.d.ts.map +1 -0
- package/dist/shaders/postprocess/blur.d.ts +3 -0
- package/dist/shaders/postprocess/blur.d.ts.map +1 -0
- package/dist/shaders/postprocess/mod.d.ts +17 -0
- package/dist/shaders/postprocess/mod.d.ts.map +1 -0
- package/dist/shaders/samplers.d.ts +3 -0
- package/dist/shaders/samplers.d.ts.map +1 -0
- package/dist/shaders/wgsl/example.wgsl.d.ts +3 -0
- package/dist/shaders/wgsl/example.wgsl.d.ts.map +1 -0
- package/dist/shaders/wgsl/hello.wgsl.d.ts +3 -0
- package/dist/shaders/wgsl/hello.wgsl.d.ts.map +1 -0
- package/dist/shaders/wgsl/helloInstanced.wgsl.d.ts +3 -0
- package/dist/shaders/wgsl/helloInstanced.wgsl.d.ts.map +1 -0
- package/dist/shaders/wgsl/quad.wgsl.d.ts +3 -0
- package/dist/shaders/wgsl/quad.wgsl.d.ts.map +1 -0
- package/dist/src/Toodle.d.ts +303 -0
- package/dist/src/Toodle.d.ts.map +1 -0
- package/dist/src/colors/mod.d.ts +871 -0
- package/dist/src/coreTypes/Color.d.ts +6 -0
- package/dist/src/coreTypes/Color.d.ts.map +1 -0
- package/dist/src/coreTypes/Point.d.ts +7 -0
- package/dist/src/coreTypes/Point.d.ts.map +1 -0
- package/dist/src/coreTypes/Size.d.ts +4 -0
- package/dist/src/coreTypes/Size.d.ts.map +1 -0
- package/dist/src/coreTypes/Transform.d.ts +15 -0
- package/dist/src/coreTypes/Transform.d.ts.map +1 -0
- package/dist/src/coreTypes/Vec2.d.ts +7 -0
- package/dist/src/coreTypes/Vec2.d.ts.map +1 -0
- package/dist/src/coreTypes/mod.d.ts +5 -0
- package/dist/src/coreTypes/mod.d.ts.map +1 -0
- package/dist/src/limits.d.ts +22 -0
- package/dist/src/limits.d.ts.map +1 -0
- package/dist/src/math/angle.d.ts +12 -0
- package/dist/src/math/angle.d.ts.map +1 -0
- package/dist/src/math/matrix.d.ts +25 -0
- package/dist/src/math/matrix.d.ts.map +1 -0
- package/dist/src/math/mod.d.ts +2 -0
- package/dist/src/math/mod.d.ts.map +1 -0
- package/dist/src/mod.d.ts +16 -0
- package/dist/src/mod.d.ts.map +1 -0
- package/dist/src/postprocess.d.ts +10 -0
- package/dist/src/postprocess.d.ts.map +1 -0
- package/dist/src/scene/Batcher.d.ts +19 -0
- package/dist/src/scene/Batcher.d.ts.map +1 -0
- package/dist/src/scene/Camera.d.ts +15 -0
- package/dist/src/scene/Camera.d.ts.map +1 -0
- package/dist/src/scene/JumboQuadNode.d.ts +28 -0
- package/dist/src/scene/QuadNode.d.ts +158 -0
- package/dist/src/scene/QuadNode.d.ts.map +1 -0
- package/dist/src/scene/RenderComponent.d.ts +10 -0
- package/dist/src/scene/RenderComponent.d.ts.map +1 -0
- package/dist/src/scene/SceneNode.d.ts +299 -0
- package/dist/src/scene/SceneNode.d.ts.map +1 -0
- package/dist/src/scene/mod.d.ts +4 -0
- package/dist/src/scene/mod.d.ts.map +1 -0
- package/dist/src/screen/mod.d.ts +1 -0
- package/dist/src/screen/mod.d.ts.map +1 -0
- package/dist/src/screen/resolution.d.ts +4 -0
- package/dist/src/screen/resolution.d.ts.map +1 -0
- package/dist/src/shaders/EngineUniform.d.ts +8 -0
- package/dist/src/shaders/EngineUniform.d.ts.map +1 -0
- package/dist/src/shaders/IShader.d.ts +14 -0
- package/dist/src/shaders/IShader.d.ts.map +1 -0
- package/dist/src/shaders/QuadShader.d.ts +17 -0
- package/dist/src/shaders/QuadShader.d.ts.map +1 -0
- package/dist/src/shaders/ShaderDescriptor.d.ts +6 -0
- package/dist/src/shaders/ShaderDescriptor.d.ts.map +1 -0
- package/dist/src/shaders/mod.d.ts +5 -0
- package/dist/src/shaders/mod.d.ts.map +1 -0
- package/dist/src/shaders/parser.d.ts +7 -0
- package/dist/src/shaders/parser.d.ts.map +1 -0
- package/dist/src/shaders/postprocess/blur.d.ts +2 -0
- package/dist/src/shaders/postprocess/mod.d.ts +16 -0
- package/dist/src/shaders/postprocess/postprocess.d.ts +8 -0
- package/dist/src/shaders/postprocess/util.d.ts +2 -0
- package/dist/src/shaders/samplers.d.ts +2 -0
- package/dist/src/shaders/samplers.d.ts.map +1 -0
- package/dist/src/shaders/wgsl/example.wgsl.d.ts +2 -0
- package/dist/src/shaders/wgsl/example.wgsl.d.ts.map +1 -0
- package/dist/src/shaders/wgsl/hello.wgsl.d.ts +2 -0
- package/dist/src/shaders/wgsl/hello.wgsl.d.ts.map +1 -0
- package/dist/src/shaders/wgsl/helloInstanced.wgsl.d.ts +2 -0
- package/dist/src/shaders/wgsl/helloInstanced.wgsl.d.ts.map +1 -0
- package/dist/src/shaders/wgsl/quad.wgsl.d.ts +2 -0
- package/dist/src/shaders/wgsl/quad.wgsl.d.ts.map +1 -0
- package/dist/src/text/FontPipeline.d.ts +13 -0
- package/dist/src/text/FontPipeline.d.ts.map +1 -0
- package/dist/src/text/MsdfFont.d.ts +81 -0
- package/dist/src/text/MsdfFont.d.ts.map +1 -0
- package/dist/src/text/TextFormatting.d.ts +18 -0
- package/dist/src/text/TextFormatting.d.ts.map +1 -0
- package/dist/src/text/TextNode.d.ts +18 -0
- package/dist/src/text/TextNode.d.ts.map +1 -0
- package/dist/src/text/TextShader.d.ts +14 -0
- package/dist/src/text/TextShader.d.ts.map +1 -0
- package/dist/src/text/mod.d.ts +3 -0
- package/dist/src/text/mod.d.ts.map +1 -0
- package/dist/src/text/shaping.d.ts +38 -0
- package/dist/src/text/shaping.d.ts.map +1 -0
- package/dist/src/text/text.wgsl.d.ts +2 -0
- package/dist/src/text/text.wgsl.d.ts.map +1 -0
- package/dist/src/textures/AssetManager.d.ts +181 -0
- package/dist/src/textures/AssetManager.d.ts.map +1 -0
- package/dist/src/textures/NewTextureComputeShader.d.ts +28 -0
- package/dist/src/textures/TextureComputeShader.d.ts +20 -0
- package/dist/src/textures/TextureComputeShader.d.ts.map +1 -0
- package/dist/src/textures/crop.wgsl.d.ts +2 -0
- package/dist/src/textures/mod.d.ts +1 -0
- package/dist/src/textures/mod.d.ts.map +1 -0
- package/dist/src/textures/pixel-scraping.wgsl.d.ts +2 -0
- package/dist/src/textures/pixel-scraping.wgsl.d.ts.map +1 -0
- package/dist/src/textures/texture-processing.wgsl.d.ts +2 -0
- package/dist/src/textures/types.d.ts +176 -0
- package/dist/src/textures/types.d.ts.map +1 -0
- package/dist/src/textures/util.d.ts +7 -0
- package/dist/src/textures/util.d.ts.map +1 -0
- package/dist/src/utils/assert.d.ts +1 -0
- package/dist/src/utils/assert.d.ts.map +1 -0
- package/dist/src/utils/boilerplate.d.ts +10 -0
- package/dist/src/utils/boilerplate.d.ts.map +1 -0
- package/dist/src/utils/error.d.ts +7 -0
- package/dist/src/utils/error.d.ts.map +1 -0
- package/dist/src/utils/mod.d.ts +2 -0
- package/dist/src/utils/mod.d.ts.map +1 -0
- package/dist/src/utils/pool.d.ts +22 -0
- package/dist/src/utils/pool.d.ts.map +1 -0
- package/dist/test/math/matrix.test.d.ts +1 -0
- package/dist/test/scene/Batcher.test.d.ts +1 -0
- package/dist/test/scene/SceneNode.test.d.ts +1 -0
- package/dist/test/shader/parser.test.d.ts +1 -0
- package/dist/text/FontPipeline.d.ts +14 -0
- package/dist/text/FontPipeline.d.ts.map +1 -0
- package/dist/text/MsdfFont.d.ts +82 -0
- package/dist/text/MsdfFont.d.ts.map +1 -0
- package/dist/text/TextFormatting.d.ts +19 -0
- package/dist/text/TextFormatting.d.ts.map +1 -0
- package/dist/text/TextNode.d.ts +19 -0
- package/dist/text/TextNode.d.ts.map +1 -0
- package/dist/text/TextShader.d.ts +15 -0
- package/dist/text/TextShader.d.ts.map +1 -0
- package/dist/text/mod.d.ts +4 -0
- package/dist/text/mod.d.ts.map +1 -0
- package/dist/text/shaping.d.ts +39 -0
- package/dist/text/shaping.d.ts.map +1 -0
- package/dist/text/text.wgsl.d.ts +3 -0
- package/dist/text/text.wgsl.d.ts.map +1 -0
- package/dist/textures/AssetManager.d.ts +182 -0
- package/dist/textures/AssetManager.d.ts.map +1 -0
- package/dist/textures/TextureComputeShader.d.ts +21 -0
- package/dist/textures/TextureComputeShader.d.ts.map +1 -0
- package/dist/textures/mod.d.ts +2 -0
- package/dist/textures/mod.d.ts.map +1 -0
- package/dist/textures/pixel-scraping.wgsl.d.ts +3 -0
- package/dist/textures/pixel-scraping.wgsl.d.ts.map +1 -0
- package/dist/textures/types.d.ts +177 -0
- package/dist/textures/types.d.ts.map +1 -0
- package/dist/textures/util.d.ts +8 -0
- package/dist/textures/util.d.ts.map +1 -0
- package/dist/utils/assert.d.ts +2 -0
- package/dist/utils/assert.d.ts.map +1 -0
- package/dist/utils/boilerplate.d.ts +11 -0
- package/dist/utils/boilerplate.d.ts.map +1 -0
- package/dist/utils/error.d.ts +8 -0
- package/dist/utils/error.d.ts.map +1 -0
- package/dist/utils/mod.d.ts +3 -0
- package/dist/utils/mod.d.ts.map +1 -0
- package/dist/utils/pool.d.ts +23 -0
- package/dist/utils/pool.d.ts.map +1 -0
- package/package.json +47 -0
- package/src/Toodle.ts +853 -0
- package/src/colors/mod.ts +151 -0
- package/src/coreTypes/Color.ts +1 -0
- package/src/coreTypes/Point.ts +7 -0
- package/src/coreTypes/Size.ts +4 -0
- package/src/coreTypes/Transform.ts +16 -0
- package/src/coreTypes/Vec2.ts +7 -0
- package/src/coreTypes/mod.ts +5 -0
- package/src/globals.d.ts +4 -0
- package/src/limits.ts +23 -0
- package/src/math/angle.ts +17 -0
- package/src/math/matrix.ts +99 -0
- package/src/math/mod.ts +2 -0
- package/src/mod.ts +22 -0
- package/src/scene/Batcher.ts +61 -0
- package/src/scene/Camera.ts +69 -0
- package/src/scene/JumboQuadNode.ts +219 -0
- package/src/scene/QuadNode.ts +403 -0
- package/src/scene/RenderComponent.ts +12 -0
- package/src/scene/SceneNode.ts +668 -0
- package/src/scene/mod.ts +4 -0
- package/src/screen/mod.ts +1 -0
- package/src/screen/resolution.ts +1 -0
- package/src/shaders/EngineUniform.ts +11 -0
- package/src/shaders/IShader.ts +20 -0
- package/src/shaders/QuadShader.ts +288 -0
- package/src/shaders/ShaderDescriptor.ts +6 -0
- package/src/shaders/mod.ts +5 -0
- package/src/shaders/parser.ts +221 -0
- package/src/shaders/postprocess/blur.ts +245 -0
- package/src/shaders/postprocess/mod.ts +71 -0
- package/src/shaders/samplers.ts +13 -0
- package/src/shaders/wgsl/example.wgsl.ts +24 -0
- package/src/shaders/wgsl/hello.wgsl.ts +62 -0
- package/src/shaders/wgsl/helloInstanced.wgsl.ts +46 -0
- package/src/shaders/wgsl/quad.wgsl.ts +140 -0
- package/src/text/FontPipeline.ts +212 -0
- package/src/text/MsdfFont.ts +190 -0
- package/src/text/TextFormatting.ts +28 -0
- package/src/text/TextNode.ts +82 -0
- package/src/text/TextShader.ts +223 -0
- package/src/text/mod.ts +8 -0
- package/src/text/shaping.ts +280 -0
- package/src/text/text.wgsl.ts +149 -0
- package/src/textures/AssetManager.ts +746 -0
- package/src/textures/TextureComputeShader.ts +434 -0
- package/src/textures/mod.ts +1 -0
- package/src/textures/pixel-scraping.wgsl.ts +131 -0
- package/src/textures/types.ts +182 -0
- package/src/textures/util.ts +352 -0
- package/src/utils/assert.ts +5 -0
- package/src/utils/boilerplate.ts +110 -0
- package/src/utils/error.ts +14 -0
- package/src/utils/mod.ts +2 -0
- 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
|
+
}
|