@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
package/src/Toodle.ts
ADDED
|
@@ -0,0 +1,853 @@
|
|
|
1
|
+
import { type Mat3, mat3 } from "wgpu-matrix";
|
|
2
|
+
import type { Color } from "./coreTypes/Color";
|
|
3
|
+
import type { Point } from "./coreTypes/Point";
|
|
4
|
+
import type { Size } from "./coreTypes/Size";
|
|
5
|
+
import type { Limits, LimitsOptions } from "./limits";
|
|
6
|
+
import { DEFAULT_LIMITS } from "./limits";
|
|
7
|
+
import {
|
|
8
|
+
convertScreenToWorld,
|
|
9
|
+
convertWorldToScreen,
|
|
10
|
+
createProjectionMatrix,
|
|
11
|
+
} from "./math/matrix";
|
|
12
|
+
import { Batcher } from "./scene/Batcher";
|
|
13
|
+
import { Camera } from "./scene/Camera";
|
|
14
|
+
import { JumboQuadNode, type JumboQuadOptions } from "./scene/JumboQuadNode";
|
|
15
|
+
import { QuadNode, type QuadOptions } from "./scene/QuadNode";
|
|
16
|
+
import { type NodeOptions, SceneNode } from "./scene/SceneNode";
|
|
17
|
+
import type { Resolution } from "./screen/resolution";
|
|
18
|
+
import type { EngineUniform } from "./shaders/EngineUniform";
|
|
19
|
+
import type { IShader } from "./shaders/IShader";
|
|
20
|
+
import type { PostProcess } from "./shaders/postprocess/mod";
|
|
21
|
+
import { QuadShader, type QuadShaderOpts } from "./shaders/QuadShader";
|
|
22
|
+
import { TextNode, type TextOptions } from "./text/TextNode";
|
|
23
|
+
import { AssetManager, type TextureId } from "./textures/AssetManager";
|
|
24
|
+
import { initGpu } from "./utils/boilerplate";
|
|
25
|
+
import { assert, Pool } from "./utils/mod";
|
|
26
|
+
|
|
27
|
+
export class Toodle {
|
|
28
|
+
/**
|
|
29
|
+
* Asset manager. Use toodle.assets.loadTexture to load texture assets.
|
|
30
|
+
*/
|
|
31
|
+
assets: AssetManager;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* diagnostics can be used as a rough gauge for performance.
|
|
35
|
+
* besides frames, these stats are reset at the beginning of each frame.
|
|
36
|
+
*/
|
|
37
|
+
diagnostics = {
|
|
38
|
+
/** number of instanced draw calls issued last frame. lower is better */
|
|
39
|
+
drawCalls: 0,
|
|
40
|
+
/** number of pipeline switches last frame. lower is better. to reduce pipeline switches, use fewer z-indexes or fewer custom shaders */
|
|
41
|
+
pipelineSwitches: 0,
|
|
42
|
+
/** number of frames rendered */
|
|
43
|
+
frames: 0,
|
|
44
|
+
/** number of instances enqueued last frame */
|
|
45
|
+
instancesEnqueued: 0,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/** sometimes for debugging you might want to access the GPU device, this should not be necessary in normal operation */
|
|
49
|
+
debug: { device: GPUDevice; presentationFormat: GPUTextureFormat };
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Camera. This applies a 2d perspective projection matrix to any nodes drawn with toodle.draw
|
|
53
|
+
*/
|
|
54
|
+
camera = new Camera();
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* clearColor is the color that will be used to clear the screen at the beginning of each frame
|
|
58
|
+
* you can also think of this as the background color of the canvas
|
|
59
|
+
*/
|
|
60
|
+
clearColor: Color = { r: 1, g: 1, b: 1, a: 1 };
|
|
61
|
+
|
|
62
|
+
#resolution: Resolution;
|
|
63
|
+
#resizeObserver: ResizeObserver;
|
|
64
|
+
#engineUniform: EngineUniform;
|
|
65
|
+
#projectionMatrix: Mat3 = mat3.identity();
|
|
66
|
+
#batcher = new Batcher();
|
|
67
|
+
#limits: Limits;
|
|
68
|
+
#device: GPUDevice;
|
|
69
|
+
#context: GPUCanvasContext;
|
|
70
|
+
#presentationFormat: GPUTextureFormat;
|
|
71
|
+
#postprocess: PostProcess | null = null;
|
|
72
|
+
#renderPass?: GPURenderPassEncoder;
|
|
73
|
+
#encoder?: GPUCommandEncoder;
|
|
74
|
+
#pingpong!: [GPUTexture, GPUTexture];
|
|
75
|
+
#defaultFilter: GPUFilterMode;
|
|
76
|
+
#matrixPool: Pool<Mat3>;
|
|
77
|
+
#atlasSize: Size;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* it's unlikely that you want to use the constructor directly.
|
|
81
|
+
* see {@link Toodle.attach} for creating a Toodle instance that draws to a canvas.
|
|
82
|
+
*/
|
|
83
|
+
constructor(
|
|
84
|
+
device: GPUDevice,
|
|
85
|
+
context: GPUCanvasContext,
|
|
86
|
+
presentationFormat: GPUTextureFormat,
|
|
87
|
+
canvas: HTMLCanvasElement,
|
|
88
|
+
resolution: Resolution,
|
|
89
|
+
options: ToodleOptions,
|
|
90
|
+
) {
|
|
91
|
+
this.#limits = {
|
|
92
|
+
...DEFAULT_LIMITS,
|
|
93
|
+
...options.limits,
|
|
94
|
+
};
|
|
95
|
+
this.#matrixPool = new Pool<Mat3>(
|
|
96
|
+
() => mat3.identity(),
|
|
97
|
+
this.#limits.instanceCount,
|
|
98
|
+
);
|
|
99
|
+
this.#device = device;
|
|
100
|
+
this.#context = context;
|
|
101
|
+
this.#presentationFormat = presentationFormat;
|
|
102
|
+
this.#defaultFilter = options.filter ?? "linear";
|
|
103
|
+
this.assets = new AssetManager(device, presentationFormat, this.#limits);
|
|
104
|
+
this.#atlasSize = {
|
|
105
|
+
width: this.assets.textureAtlas.width,
|
|
106
|
+
height: this.assets.textureAtlas.height,
|
|
107
|
+
};
|
|
108
|
+
this.debug = { device, presentationFormat };
|
|
109
|
+
this.#engineUniform = {
|
|
110
|
+
resolution,
|
|
111
|
+
camera: this.camera,
|
|
112
|
+
viewProjectionMatrix: mat3.identity(),
|
|
113
|
+
};
|
|
114
|
+
this.#resolution = resolution;
|
|
115
|
+
|
|
116
|
+
this.#createPingPongTextures(resolution);
|
|
117
|
+
this.resize(this.#resolution);
|
|
118
|
+
|
|
119
|
+
this.#resizeObserver = this.#createResizeObserver(canvas);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Screen shader is an optional slot for post-processing effects.
|
|
124
|
+
* Note that this will do the main render pass to an offscreen texture, which may impact performance.
|
|
125
|
+
*/
|
|
126
|
+
get postprocess() {
|
|
127
|
+
return this.#postprocess;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
set postprocess(value: PostProcess | null) {
|
|
131
|
+
this.#postprocess = value;
|
|
132
|
+
const hasChanged =
|
|
133
|
+
this.#resolution.width !==
|
|
134
|
+
this.#pingpong[0].width / window.devicePixelRatio ||
|
|
135
|
+
this.#resolution.height !==
|
|
136
|
+
this.#pingpong[0].height / window.devicePixelRatio;
|
|
137
|
+
if (value != null && hasChanged) {
|
|
138
|
+
this.#createPingPongTextures(this.#resolution);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* call resize when the canvas is resized.
|
|
144
|
+
* this will update the projection matrix and the resolution.
|
|
145
|
+
*
|
|
146
|
+
* @param resolution - the resolution of the canvas in logical pixels.
|
|
147
|
+
* this should be `canvas.clientWidth x canvas.clientHeight` and NOT `canvas.width * canvas.height`
|
|
148
|
+
*
|
|
149
|
+
* @example
|
|
150
|
+
*
|
|
151
|
+
* const canvas = document.querySelector("canvas")!
|
|
152
|
+
*
|
|
153
|
+
* const observer = new ResizeObserver((entries) => {
|
|
154
|
+
* if (entries.length === 0) return
|
|
155
|
+
* toodle.resize({ width: canvas.clientWidth, height: canvas.clientHeight })
|
|
156
|
+
* })
|
|
157
|
+
*
|
|
158
|
+
* observer.observe(canvas)
|
|
159
|
+
*/
|
|
160
|
+
resize(resolution: Resolution) {
|
|
161
|
+
createProjectionMatrix(resolution, this.#projectionMatrix);
|
|
162
|
+
|
|
163
|
+
const hasChanged =
|
|
164
|
+
resolution.width !== this.#resolution.width ||
|
|
165
|
+
resolution.height !== this.#resolution.height;
|
|
166
|
+
if (this.postprocess && hasChanged) {
|
|
167
|
+
this.#pingpong[0].destroy();
|
|
168
|
+
this.#pingpong[1].destroy();
|
|
169
|
+
}
|
|
170
|
+
this.#resolution = resolution;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
#createPingPongTextures(resolution: Resolution) {
|
|
174
|
+
if (this.#pingpong?.length) {
|
|
175
|
+
this.#pingpong[0].destroy();
|
|
176
|
+
this.#pingpong[1].destroy();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
this.#pingpong = [
|
|
180
|
+
this.#device.createTexture({
|
|
181
|
+
size: {
|
|
182
|
+
width: resolution.width * window.devicePixelRatio,
|
|
183
|
+
height: resolution.height * window.devicePixelRatio,
|
|
184
|
+
},
|
|
185
|
+
format: this.#presentationFormat,
|
|
186
|
+
usage:
|
|
187
|
+
GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT,
|
|
188
|
+
}),
|
|
189
|
+
this.#device.createTexture({
|
|
190
|
+
size: {
|
|
191
|
+
width: resolution.width * window.devicePixelRatio,
|
|
192
|
+
height: resolution.height * window.devicePixelRatio,
|
|
193
|
+
},
|
|
194
|
+
format: this.#presentationFormat,
|
|
195
|
+
usage:
|
|
196
|
+
GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT,
|
|
197
|
+
}),
|
|
198
|
+
];
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
#createResizeObserver(canvas: HTMLCanvasElement) {
|
|
202
|
+
// see https://webgpufundamentals.org/webgpu/lessons/webgpu-resizing-the-canvas.html
|
|
203
|
+
// for explanation of incorporating devicePixelRatio
|
|
204
|
+
const resizeObserver = new ResizeObserver((entries) => {
|
|
205
|
+
if (!entries || !entries.length) return;
|
|
206
|
+
for (const entry of entries) {
|
|
207
|
+
const width =
|
|
208
|
+
entry.devicePixelContentBoxSize?.[0].inlineSize ||
|
|
209
|
+
entry.contentBoxSize[0].inlineSize * devicePixelRatio;
|
|
210
|
+
const height =
|
|
211
|
+
entry.devicePixelContentBoxSize?.[0].blockSize ||
|
|
212
|
+
entry.contentBoxSize[0].blockSize * devicePixelRatio;
|
|
213
|
+
|
|
214
|
+
// uncomment these lines to debug issues with high dpi monitors
|
|
215
|
+
// and OS zoom
|
|
216
|
+
//
|
|
217
|
+
// const rect = entry.target.getBoundingClientRect();
|
|
218
|
+
// console.table({
|
|
219
|
+
// width,
|
|
220
|
+
// clientWidth: canvas.clientWidth,
|
|
221
|
+
// contentBoxInlineSize: entry.contentBoxSize[0].inlineSize,
|
|
222
|
+
// rectWidth: rect.width,
|
|
223
|
+
// height,
|
|
224
|
+
// clientHeight: canvas.clientHeight,
|
|
225
|
+
// contentBoxBlockSize: entry.contentBoxSize[0].blockSize,
|
|
226
|
+
// rectHeight: rect.height,
|
|
227
|
+
// });
|
|
228
|
+
|
|
229
|
+
if (canvas.width !== width || canvas.height !== height) {
|
|
230
|
+
canvas.width = Math.max(1, width);
|
|
231
|
+
canvas.height = Math.max(1, height);
|
|
232
|
+
}
|
|
233
|
+
this.resize({ width: canvas.clientWidth, height: canvas.clientHeight });
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
try {
|
|
237
|
+
resizeObserver.observe(canvas, { box: "device-pixel-content-box" });
|
|
238
|
+
} catch {
|
|
239
|
+
resizeObserver.observe(canvas);
|
|
240
|
+
}
|
|
241
|
+
return resizeObserver;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* The resolution of the canvas in css or logical pixels.
|
|
246
|
+
*
|
|
247
|
+
* @example
|
|
248
|
+
*
|
|
249
|
+
* // devicePixelRatio is 1, resolution is 200x200
|
|
250
|
+
* <canvas width="200" height="200" style="width: 200px; height: 200px;"></canvas>
|
|
251
|
+
* // devicePixelRatio is 2, resolution is 100x100
|
|
252
|
+
* <canvas width="200" height="200" style="width: 100px; height: 100px;"></canvas>
|
|
253
|
+
*/
|
|
254
|
+
get resolution() {
|
|
255
|
+
return this.#resolution;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Returns the currently configured Toodle engine limits
|
|
260
|
+
*
|
|
261
|
+
* See: https://toodle.gg/f849595b3ed13fc956fc1459a5cb5f0228f9d259/limits.html
|
|
262
|
+
*
|
|
263
|
+
* @example
|
|
264
|
+
*
|
|
265
|
+
* const instanceLimit: number = toodle.limits.instanceCount;
|
|
266
|
+
*/
|
|
267
|
+
get limits(): Limits {
|
|
268
|
+
return this.#limits;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
get batcher(): Batcher {
|
|
272
|
+
return this.#batcher;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* The maximum number of pixels that can be loaded into texture atlases for this toodle instance.
|
|
277
|
+
*/
|
|
278
|
+
get maxPixels() {
|
|
279
|
+
return (
|
|
280
|
+
this.#limits.textureSize *
|
|
281
|
+
this.#limits.textureSize *
|
|
282
|
+
this.#limits.textureArrayLayers
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* The maximum amount of GPU memory that will be used by the Toodle instance.
|
|
288
|
+
* This is a rough estimate and may not be exact. This will be allocated up front when calling Toodle.attach, and freed when calling toodle.destroy.
|
|
289
|
+
*/
|
|
290
|
+
get maxGpuMemory() {
|
|
291
|
+
return (
|
|
292
|
+
this.#limits.instanceCount *
|
|
293
|
+
this.#limits.instanceBufferSize *
|
|
294
|
+
this.#limits.shaderCount
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* call startFrame before drawing anything.
|
|
300
|
+
* this will create a new encoder and render pass.
|
|
301
|
+
*
|
|
302
|
+
* @example
|
|
303
|
+
*
|
|
304
|
+
* toodle.startFrame();
|
|
305
|
+
* // draw stuff
|
|
306
|
+
* toodle.endFrame();
|
|
307
|
+
*/
|
|
308
|
+
startFrame(options?: StartFrameOptions) {
|
|
309
|
+
this.#encoder = this.#device.createCommandEncoder();
|
|
310
|
+
const target = this.postprocess
|
|
311
|
+
? this.#pingpong[0]
|
|
312
|
+
: this.#context.getCurrentTexture();
|
|
313
|
+
this.#renderPass = this.#encoder.beginRenderPass({
|
|
314
|
+
label: `toodle frame ${this.diagnostics.frames}`,
|
|
315
|
+
colorAttachments: [
|
|
316
|
+
{
|
|
317
|
+
view: target.createView(),
|
|
318
|
+
clearValue: this.clearColor,
|
|
319
|
+
loadOp: options?.loadOp ?? "clear",
|
|
320
|
+
storeOp: "store",
|
|
321
|
+
},
|
|
322
|
+
],
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
this.diagnostics.drawCalls =
|
|
326
|
+
this.diagnostics.pipelineSwitches =
|
|
327
|
+
this.diagnostics.instancesEnqueued =
|
|
328
|
+
0;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* call draw in between start frame and end frame to enqueue an instanced draw call.
|
|
333
|
+
*
|
|
334
|
+
* @example
|
|
335
|
+
*
|
|
336
|
+
* toodle.assets.loadTexture("myImage", "assets/image.png");
|
|
337
|
+
* const quad = toodle.Quad("myImage");
|
|
338
|
+
*
|
|
339
|
+
* toodle.startFrame();
|
|
340
|
+
* toodle.draw(quad);
|
|
341
|
+
* toodle.endFrame();
|
|
342
|
+
*/
|
|
343
|
+
draw(node: SceneNode) {
|
|
344
|
+
if (node instanceof QuadNode) {
|
|
345
|
+
node.assetManager.validateTextureReference(node);
|
|
346
|
+
} else {
|
|
347
|
+
this.assets.validateTextureReference(node);
|
|
348
|
+
}
|
|
349
|
+
this.#batcher.enqueue(node);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* call end frame to run through enqueued draw calls and submit them to the GPU.
|
|
354
|
+
*
|
|
355
|
+
* @example
|
|
356
|
+
*
|
|
357
|
+
* toodle.startFrame();
|
|
358
|
+
* // draw stuff
|
|
359
|
+
* toodle.endFrame();
|
|
360
|
+
*/
|
|
361
|
+
endFrame() {
|
|
362
|
+
try {
|
|
363
|
+
assert(
|
|
364
|
+
this.#renderPass,
|
|
365
|
+
"no render pass found. have you called startFrame?",
|
|
366
|
+
);
|
|
367
|
+
assert(this.#encoder, "no encoder found. have you called startFrame?");
|
|
368
|
+
|
|
369
|
+
mat3.mul(
|
|
370
|
+
this.#projectionMatrix,
|
|
371
|
+
this.camera.matrix,
|
|
372
|
+
this.#engineUniform.viewProjectionMatrix,
|
|
373
|
+
);
|
|
374
|
+
this.#engineUniform.resolution = this.#resolution;
|
|
375
|
+
for (const pipeline of this.#batcher.pipelines) {
|
|
376
|
+
pipeline.shader.startFrame(this.#device, this.#engineUniform);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
this.diagnostics.instancesEnqueued = this.#batcher.nodes.length;
|
|
380
|
+
if (this.#batcher.nodes.length > this.#limits.instanceCount) {
|
|
381
|
+
const err = new Error(
|
|
382
|
+
`ToodleInstanceCap: ${this.batcher.nodes.length} instances enqueued, max is ${this.limits.instanceCount}`,
|
|
383
|
+
);
|
|
384
|
+
err.name = "ToodleInstanceCap";
|
|
385
|
+
throw err;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
for (const layer of this.#batcher.layers) {
|
|
389
|
+
for (const pipeline of layer.pipelines) {
|
|
390
|
+
this.diagnostics.pipelineSwitches++;
|
|
391
|
+
this.diagnostics.drawCalls += pipeline.shader.processBatch(
|
|
392
|
+
this.#renderPass,
|
|
393
|
+
pipeline.nodes,
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
for (const pipeline of this.#batcher.pipelines) {
|
|
399
|
+
pipeline.shader.endFrame();
|
|
400
|
+
}
|
|
401
|
+
this.#renderPass.end();
|
|
402
|
+
|
|
403
|
+
if (this.postprocess) {
|
|
404
|
+
this.postprocess.process(
|
|
405
|
+
this.#device.queue,
|
|
406
|
+
this.#encoder,
|
|
407
|
+
this.#pingpong,
|
|
408
|
+
this.#context.getCurrentTexture(),
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
this.#device.queue.submit([this.#encoder.finish()]);
|
|
412
|
+
} finally {
|
|
413
|
+
this.#batcher.flush();
|
|
414
|
+
this.#matrixPool.free();
|
|
415
|
+
this.diagnostics.frames++;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Convert a point from one coordinate space to another.
|
|
421
|
+
*
|
|
422
|
+
* @param point - The point to convert.
|
|
423
|
+
* @param options - The options for the conversion.
|
|
424
|
+
* @returns The converted point.
|
|
425
|
+
*/
|
|
426
|
+
convertSpace(
|
|
427
|
+
point: Point,
|
|
428
|
+
options: { from: "screen" | "world"; to: "world" | "screen" },
|
|
429
|
+
): Point {
|
|
430
|
+
if (options.from === "screen" && options.to === "world") {
|
|
431
|
+
return convertScreenToWorld(
|
|
432
|
+
point,
|
|
433
|
+
this.camera,
|
|
434
|
+
this.#projectionMatrix,
|
|
435
|
+
this.#resolution,
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
if (options.from === "world" && options.to === "screen") {
|
|
440
|
+
return convertWorldToScreen(
|
|
441
|
+
point,
|
|
442
|
+
this.camera,
|
|
443
|
+
this.#projectionMatrix,
|
|
444
|
+
this.#resolution,
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
if (options.from === options.to) {
|
|
449
|
+
return point;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
throw new Error(
|
|
453
|
+
`Unknown conversion from: ${options.from} to: ${options.to}`,
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* The number of frames rendered since this Toodle instance was created.
|
|
459
|
+
*/
|
|
460
|
+
get frameCount() {
|
|
461
|
+
return this.diagnostics.frames;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Create a custom shader for quad instances. In some engines, this might be called a material.
|
|
466
|
+
*
|
|
467
|
+
* @param label Debug name of the shader
|
|
468
|
+
* @param instanceCount - The maximum number of instances that will be processed by the shader. Note that a worst-case buffer of this many instances will be immediately allocated.
|
|
469
|
+
* @param userCode - The WGSL code to be used for the shader.
|
|
470
|
+
* @param blendMode - The blend mode to be used for the shader.
|
|
471
|
+
*
|
|
472
|
+
* @example
|
|
473
|
+
*
|
|
474
|
+
*
|
|
475
|
+
*/
|
|
476
|
+
QuadShader(
|
|
477
|
+
label: string,
|
|
478
|
+
instanceCount: number,
|
|
479
|
+
userCode: string,
|
|
480
|
+
shaderOpts?: QuadShaderOpts,
|
|
481
|
+
) {
|
|
482
|
+
return new QuadShader(
|
|
483
|
+
label,
|
|
484
|
+
shaderOpts?.assetManager ?? this.assets,
|
|
485
|
+
this.#device,
|
|
486
|
+
this.#presentationFormat,
|
|
487
|
+
userCode,
|
|
488
|
+
instanceCount,
|
|
489
|
+
shaderOpts?.blendMode,
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Create a new quad node.
|
|
495
|
+
*
|
|
496
|
+
* @param assetId - The ID of the asset to use for the quad. This must have been loaded with toodle.assets.loadBundle.
|
|
497
|
+
*
|
|
498
|
+
* @param options - QuadOptions for Quad creation
|
|
499
|
+
* @param options
|
|
500
|
+
* @example
|
|
501
|
+
*
|
|
502
|
+
* await toodle.assets.loadTextures({
|
|
503
|
+
* "myImage": new URL("assets/image.png"),
|
|
504
|
+
* });
|
|
505
|
+
* const quad = toodle.Quad("myImage");
|
|
506
|
+
*
|
|
507
|
+
* toodle.startFrame();
|
|
508
|
+
* toodle.draw(quad);
|
|
509
|
+
* toodle.endFrame();
|
|
510
|
+
*/
|
|
511
|
+
Quad(assetId: TextureId, options: QuadOptions = {}) {
|
|
512
|
+
const assetManager = options.assetManager ?? this.assets;
|
|
513
|
+
options.idealSize ??= assetManager.getSize(assetId);
|
|
514
|
+
options.shader ??= this.#defaultQuadShader();
|
|
515
|
+
options.atlasCoords ??= assetManager.extra.getAtlasCoords(assetId)[0];
|
|
516
|
+
options.textureId ??= assetId;
|
|
517
|
+
options.cropOffset ??= assetManager.extra.getTextureOffset(assetId);
|
|
518
|
+
options.assetManager = assetManager;
|
|
519
|
+
|
|
520
|
+
options.atlasSize = this.#atlasSize;
|
|
521
|
+
options.region ??= {
|
|
522
|
+
x: 0,
|
|
523
|
+
y: 0,
|
|
524
|
+
width: options.atlasCoords.uvScale.width * this.#atlasSize.width,
|
|
525
|
+
height: options.atlasCoords.uvScale.height * this.#atlasSize.height,
|
|
526
|
+
};
|
|
527
|
+
|
|
528
|
+
options.assetManager = assetManager;
|
|
529
|
+
const quad = new QuadNode(options, this.#matrixPool);
|
|
530
|
+
return quad;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* Create a jumbo quad node. This contains multiple tiles for a single texture.
|
|
535
|
+
*
|
|
536
|
+
* @param assetId - The ID of the asset to use for the jumbo quad. This must have been loaded with toodle.assets.loadTextures.
|
|
537
|
+
*
|
|
538
|
+
* @param options - QuadOptions for Quad creation
|
|
539
|
+
*
|
|
540
|
+
*/
|
|
541
|
+
JumboQuad(assetId: TextureId, options: JumboQuadOptions) {
|
|
542
|
+
options.shader ??= this.#defaultQuadShader();
|
|
543
|
+
options.textureId ??= assetId;
|
|
544
|
+
options.cropOffset ??= {
|
|
545
|
+
x: 0,
|
|
546
|
+
y: 0,
|
|
547
|
+
};
|
|
548
|
+
options.tiles ??= [];
|
|
549
|
+
|
|
550
|
+
// this holds the size of the full texture based on all of its tiles
|
|
551
|
+
const originalSize = {
|
|
552
|
+
width: 0,
|
|
553
|
+
height: 0,
|
|
554
|
+
};
|
|
555
|
+
|
|
556
|
+
for (const tile of options.tiles) {
|
|
557
|
+
if (!tile.size) {
|
|
558
|
+
tile.size = this.assets.getSize(tile.textureId);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
if (!tile.atlasCoords) {
|
|
562
|
+
tile.atlasCoords = this.assets.extra.getAtlasCoords(tile.textureId)[0];
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
if (tile.offset.x + tile.size!.width > originalSize.width) {
|
|
566
|
+
originalSize.width = tile.offset.x + tile.size!.width;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
if (tile.offset.y + tile.size!.height > originalSize.height) {
|
|
570
|
+
originalSize.height = tile.offset.y + tile.size!.height;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
options.region ??= {
|
|
575
|
+
x: 0,
|
|
576
|
+
y: 0,
|
|
577
|
+
width: originalSize.width,
|
|
578
|
+
height: originalSize.height,
|
|
579
|
+
};
|
|
580
|
+
|
|
581
|
+
options.idealSize ??= {
|
|
582
|
+
width: originalSize.width,
|
|
583
|
+
height: originalSize.height,
|
|
584
|
+
};
|
|
585
|
+
|
|
586
|
+
options.atlasSize = this.#atlasSize;
|
|
587
|
+
|
|
588
|
+
options.assetManager = this.assets;
|
|
589
|
+
|
|
590
|
+
return new JumboQuadNode(options, this.#matrixPool);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Create a new container node.
|
|
595
|
+
*
|
|
596
|
+
* @example
|
|
597
|
+
*
|
|
598
|
+
* const node = toodle.Node();
|
|
599
|
+
* const child = node.add(toodle.Node());
|
|
600
|
+
* node.position = [100, 100];
|
|
601
|
+
* console.log(child.matrix);
|
|
602
|
+
*/
|
|
603
|
+
Node(nodeOpts?: NodeOptions) {
|
|
604
|
+
return new SceneNode(nodeOpts);
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
Text(fontId: string, text: string, textOpts?: TextOptions) {
|
|
608
|
+
const shader = this.assets.getFont(fontId);
|
|
609
|
+
|
|
610
|
+
return new TextNode(shader, text, textOpts);
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
shapes = {
|
|
614
|
+
Rect: (options: QuadOptions = {}) => {
|
|
615
|
+
options.idealSize ??= { width: 1, height: 1 };
|
|
616
|
+
options.shader ??= this.#defaultQuadShader();
|
|
617
|
+
options.atlasCoords ??= {
|
|
618
|
+
atlasIndex: 1000,
|
|
619
|
+
uvOffset: { x: 0, y: 0 },
|
|
620
|
+
uvScale: { width: 0, height: 0 },
|
|
621
|
+
cropOffset: { x: 0, y: 0 },
|
|
622
|
+
originalSize: { width: 1, height: 1 },
|
|
623
|
+
};
|
|
624
|
+
options.assetManager = this.assets;
|
|
625
|
+
|
|
626
|
+
const quad = new QuadNode(options, this.#matrixPool);
|
|
627
|
+
|
|
628
|
+
if (options?.position) {
|
|
629
|
+
quad.position = options.position;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
if (options?.rotation) {
|
|
633
|
+
quad.rotation = options.rotation;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
if (options?.scale) {
|
|
637
|
+
quad.scale = options.scale;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
return quad;
|
|
641
|
+
},
|
|
642
|
+
|
|
643
|
+
Circle: (options: QuadOptions = {}) => {
|
|
644
|
+
options.idealSize ??= { width: 1, height: 1 };
|
|
645
|
+
options.shader ??= this.#defaultQuadShader();
|
|
646
|
+
options.atlasCoords ??= {
|
|
647
|
+
atlasIndex: 1001,
|
|
648
|
+
uvOffset: { x: 0, y: 0 },
|
|
649
|
+
uvScale: { width: 0, height: 0 },
|
|
650
|
+
cropOffset: { x: 0, y: 0 },
|
|
651
|
+
originalSize: { width: 1, height: 1 },
|
|
652
|
+
};
|
|
653
|
+
options.assetManager = this.assets;
|
|
654
|
+
const quad = new QuadNode(options, this.#matrixPool);
|
|
655
|
+
|
|
656
|
+
if (options?.position) {
|
|
657
|
+
quad.position = options.position;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
if (options?.rotation) {
|
|
661
|
+
quad.rotation = options.rotation;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
if (options?.scale) {
|
|
665
|
+
quad.scale = options.scale;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
return quad;
|
|
669
|
+
},
|
|
670
|
+
|
|
671
|
+
Line: (options: LineOptions) => {
|
|
672
|
+
const center = {
|
|
673
|
+
x: (options.start.x + options.end.x) / 2,
|
|
674
|
+
y: (options.start.y + options.end.y) / 2,
|
|
675
|
+
};
|
|
676
|
+
const angle = Math.atan2(
|
|
677
|
+
options.end.y - options.start.y,
|
|
678
|
+
options.end.x - options.start.x,
|
|
679
|
+
);
|
|
680
|
+
const length = Math.sqrt(
|
|
681
|
+
(options.end.x - options.start.x) ** 2 +
|
|
682
|
+
(options.end.y - options.start.y) ** 2,
|
|
683
|
+
);
|
|
684
|
+
|
|
685
|
+
const line = new QuadNode(
|
|
686
|
+
{
|
|
687
|
+
color: options.color,
|
|
688
|
+
atlasCoords: {
|
|
689
|
+
atlasIndex: 1000,
|
|
690
|
+
uvOffset: { x: 0, y: 0 },
|
|
691
|
+
uvScale: { width: 0, height: 0 },
|
|
692
|
+
cropOffset: { x: 0, y: 0 },
|
|
693
|
+
originalSize: { width: 1, height: 1 },
|
|
694
|
+
},
|
|
695
|
+
shader: options.shader ?? this.#defaultQuadShader(),
|
|
696
|
+
idealSize: { width: 1, height: 1 },
|
|
697
|
+
layer: options.layer,
|
|
698
|
+
key: options.key,
|
|
699
|
+
rotationRadians: angle,
|
|
700
|
+
assetManager: this.assets,
|
|
701
|
+
scale: {
|
|
702
|
+
x: length,
|
|
703
|
+
y: options.thickness ?? 1,
|
|
704
|
+
},
|
|
705
|
+
position: center,
|
|
706
|
+
},
|
|
707
|
+
this.#matrixPool,
|
|
708
|
+
);
|
|
709
|
+
|
|
710
|
+
return line;
|
|
711
|
+
},
|
|
712
|
+
};
|
|
713
|
+
|
|
714
|
+
#quadShader: QuadShader | null = null;
|
|
715
|
+
|
|
716
|
+
#defaultQuadShader() {
|
|
717
|
+
if (this.#quadShader) {
|
|
718
|
+
return this.#quadShader;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
const shader = this.QuadShader(
|
|
722
|
+
"default quad shader",
|
|
723
|
+
this.#limits.instanceCount,
|
|
724
|
+
/*wgsl*/ `
|
|
725
|
+
@fragment
|
|
726
|
+
fn frag(vertex: VertexOutput) -> @location(0) vec4f {
|
|
727
|
+
let color = default_fragment_shader(vertex, ${
|
|
728
|
+
this.#defaultFilter === "nearest"
|
|
729
|
+
? "nearestSampler"
|
|
730
|
+
: "linearSampler"
|
|
731
|
+
});
|
|
732
|
+
return color;
|
|
733
|
+
}
|
|
734
|
+
`,
|
|
735
|
+
);
|
|
736
|
+
|
|
737
|
+
this.#quadShader = shader;
|
|
738
|
+
return shader;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
/**
|
|
742
|
+
* Attach toodle to a canvas.
|
|
743
|
+
*
|
|
744
|
+
* @param canvas - The canvas to attach toodle to.
|
|
745
|
+
* @param options - ToodleOptions for the creation of the toodle instance
|
|
746
|
+
* @returns A promise that resolves to a Toodle instance.
|
|
747
|
+
*
|
|
748
|
+
* @example
|
|
749
|
+
*
|
|
750
|
+
* const canvas = document.createElement("canvas");
|
|
751
|
+
*
|
|
752
|
+
* const toodle = await Toodle.attach(canvas);
|
|
753
|
+
*/
|
|
754
|
+
static async attach(canvas: HTMLCanvasElement, options?: ToodleOptions) {
|
|
755
|
+
canvas.width = canvas.clientWidth * devicePixelRatio;
|
|
756
|
+
canvas.height = canvas.clientHeight * devicePixelRatio;
|
|
757
|
+
const { device, context, presentationFormat } = await initGpu(canvas);
|
|
758
|
+
return new Toodle(
|
|
759
|
+
device,
|
|
760
|
+
context,
|
|
761
|
+
presentationFormat,
|
|
762
|
+
canvas,
|
|
763
|
+
{
|
|
764
|
+
width: canvas.clientWidth,
|
|
765
|
+
height: canvas.clientHeight,
|
|
766
|
+
},
|
|
767
|
+
options || {},
|
|
768
|
+
);
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
/**
|
|
772
|
+
* Destroy the toodle instance and release its gpu and cpu resources.
|
|
773
|
+
*
|
|
774
|
+
* Note that calling any methods on the instance after this result in undefined behavior.
|
|
775
|
+
*/
|
|
776
|
+
destroy() {
|
|
777
|
+
this.#resizeObserver.disconnect();
|
|
778
|
+
this.#device.destroy();
|
|
779
|
+
this.assets.destroy();
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
/**
|
|
783
|
+
* Advanced and niche features
|
|
784
|
+
*/
|
|
785
|
+
extra = {
|
|
786
|
+
/**
|
|
787
|
+
* Get the GPU device used by this Toodle instance.
|
|
788
|
+
*/
|
|
789
|
+
device: (): GPUDevice => {
|
|
790
|
+
return this.#device;
|
|
791
|
+
},
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
export type StartFrameOptions = {
|
|
796
|
+
/**
|
|
797
|
+
* The load operation to use for the render pass.
|
|
798
|
+
*
|
|
799
|
+
* **clear**: clear the current texture to the clear color. necessary if you're using toodle without another renderer.
|
|
800
|
+
*
|
|
801
|
+
* **load**: blend the render pass with the current canvas contents. useful if you're using toodle alongside another renderer like painter or pixi.js
|
|
802
|
+
*
|
|
803
|
+
* @default "clear"
|
|
804
|
+
*
|
|
805
|
+
*/
|
|
806
|
+
loadOp?: "load" | "clear";
|
|
807
|
+
};
|
|
808
|
+
|
|
809
|
+
export type ToodleOptions = {
|
|
810
|
+
/**
|
|
811
|
+
* The filter mode to use for the default quad shader.
|
|
812
|
+
* see: https://webgpufundamentals.org/webgpu/lessons/webgpu-textures.html#a-mag-filter
|
|
813
|
+
*
|
|
814
|
+
* **nearest**: nearest neighbor sampling. makes pixel art look sharp and vector art look jaggy.
|
|
815
|
+
*
|
|
816
|
+
* **linear**: linear sampling. makes vector art look smooth and pixel art look blurry.
|
|
817
|
+
*
|
|
818
|
+
* @default "linear"
|
|
819
|
+
*/
|
|
820
|
+
filter?: "nearest" | "linear";
|
|
821
|
+
limits?: LimitsOptions;
|
|
822
|
+
};
|
|
823
|
+
|
|
824
|
+
export type LineOptions = {
|
|
825
|
+
/**
|
|
826
|
+
* The start position of the line.
|
|
827
|
+
*/
|
|
828
|
+
start: Point;
|
|
829
|
+
/**
|
|
830
|
+
* The end position of the line.
|
|
831
|
+
*/
|
|
832
|
+
end: Point;
|
|
833
|
+
/**
|
|
834
|
+
* The color of the line.
|
|
835
|
+
*/
|
|
836
|
+
color: Color;
|
|
837
|
+
/**
|
|
838
|
+
* The thickness of the line.
|
|
839
|
+
*/
|
|
840
|
+
thickness?: number;
|
|
841
|
+
/**
|
|
842
|
+
* The shader to use for the line.
|
|
843
|
+
*/
|
|
844
|
+
shader?: IShader;
|
|
845
|
+
/**
|
|
846
|
+
* The layer to draw the line on.
|
|
847
|
+
*/
|
|
848
|
+
layer?: number;
|
|
849
|
+
/**
|
|
850
|
+
* A unique identifier for the line.
|
|
851
|
+
*/
|
|
852
|
+
key?: string;
|
|
853
|
+
};
|