@codexo/exojs 0.6.2 → 0.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,156 +1,156 @@
1
- # ExoJS
2
-
3
- ExoJS is a TypeScript-first 2D runtime for browser games and interactive apps. It is designed around explicit scene flow, practical rendering features, and predictable runtime behavior.
4
-
5
- ## Project status
6
-
7
- ExoJS is **pre-1.0**. The public API is still under active design — scene graph, rendering pipeline, and resource lifecycle boundaries may change between minor versions. Pin exact versions in downstream experiments. `1.0.0` will mark the first stable API contract.
8
-
9
- ## Why ExoJS
10
-
11
- - TypeScript-first API surface with strong runtime contracts
12
- - Scene and asset workflows built for real game loops
13
- - Modern rendering stack: WebGPU-first with WebGL2 fallback
14
- - Practical visuals: filters, masks, render passes, cache-as-bitmap
15
- - Gameplay tools: animated sprites, scene stacking, camera helpers, audio sprites
16
- - Performance visibility with built-in render stats and benchmark harness
17
- - Optional Rapier physics integration without forcing physics on every app
18
-
19
- ## What Is Shipped Today
20
-
21
- - `Application`, `Scene`, and scene-manager lifecycle
22
- - Typed `Loader` with manifest/bundle workflow (`defineAssetManifest`, `registerManifest`, `loadBundle`)
23
- - Drawables: `Sprite`, `AnimatedSprite`, `Graphics`, `ParticleSystem`, `Text`, `Video`
24
- - Scene stacking (`overlay` / `modal` / `opaque`) with input routing and fade transitions
25
- - View/camera helpers (`follow`, bounds clamp, shake, zoom)
26
- - Rendering composition primitives (`RenderTexture`, `RenderTargetPass`, filter chains, visual masks, cache-as-bitmap)
27
- - Render stats (`submittedNodes`, `culledNodes`, `drawCalls`, `batches`, `renderPasses`, ...)
28
- - Optional Rapier adapter (`createRapierPhysicsWorld`)
29
-
30
- ## Installation
31
-
32
- ```bash
33
- npm install @codexo/exojs
34
- ```
35
-
36
- ExoJS currently publishes an ESM-first package shape. Use `import` syntax with modern bundlers/runtime tooling.
37
- CommonJS `require()` usage is not part of the supported contract for this pre-1.0 line.
38
-
39
- ## Quickstart
40
-
41
- ```ts
42
- import { Application, Scene, Graphics, Color, type RenderBackend } from '@codexo/exojs';
43
-
44
- class HelloScene extends Scene {
45
- private readonly box = new Graphics();
46
-
47
- public constructor() {
48
- super();
49
-
50
- this.box.fillColor = Color.white;
51
- this.box.drawRectangle(-32, -32, 64, 64);
52
- this.box.setPosition(400, 300);
53
-
54
- this.addChild(this.box);
55
- }
56
-
57
- public override update(delta: import('@codexo/exojs').Time): void {
58
- this.box.rotation += delta.seconds * 45;
59
- }
60
-
61
- public override draw(backend: RenderBackend): void {
62
- this.root.render(backend);
63
- }
64
- }
65
-
66
- const canvas = document.querySelector('canvas');
67
-
68
- if (!canvas) {
69
- throw new Error('Missing <canvas> element.');
70
- }
71
-
72
- const app = new Application({
73
- canvas,
74
- width: 800,
75
- height: 600,
76
- clearColor: Color.cornflowerBlue,
77
- });
78
-
79
- await app.start(new HelloScene());
80
- ```
81
-
82
- ## Next Steps
83
-
84
- - In-repo examples: [examples/README.md](examples/README.md)
85
-
86
- ## WebGPU and WebGL2
87
-
88
- `Application` defaults to backend auto-selection:
89
-
90
- - prefers WebGPU when available
91
- - falls back to WebGL2 if WebGPU is unavailable or initialization fails
92
-
93
- You can force backend selection when needed:
94
-
95
- ```ts
96
- new Application({ backend: { type: 'webgpu' } });
97
- new Application({ backend: { type: 'webgl2' } });
98
- new Application({ backend: { type: 'auto' } });
99
- ```
100
-
101
- ## Optional Rapier Physics
102
-
103
- Rapier integration is opt-in and loaded only when you use it.
104
-
105
- ```ts
106
- import { createRapierPhysicsWorld } from '@codexo/exojs';
107
-
108
- const physics = await createRapierPhysicsWorld({ gravityY: 9.81 });
109
- ```
110
-
111
- If Rapier is unavailable, creation fails with a clear setup error.
112
-
113
- ### Physics scope policy
114
-
115
- ExoJS ships **one** physics adapter: Rapier. The integration is intentionally
116
- narrow:
117
-
118
- - Physics is **optional**. `@dimforge/rapier2d-compat` is a peer dependency
119
- marked `optional`. Apps that do not call `createRapierPhysicsWorld` never
120
- load it and never pay for it at runtime.
121
- - Rendering, application, and core scene code **do not** depend on physics.
122
- The adapter binds Rapier bodies to scene nodes from the outside; the core
123
- has no knowledge of physics.
124
- - ExoJS is **not** a physics-engine abstraction layer. There is no
125
- `PhysicsWorld` interface that spans multiple backends, and no plan to
126
- add one. If you need a different physics library, integrate it directly
127
- in your app code without library involvement.
128
- - A second physics adapter (Box2D, Matter.js, Planck, etc.) is **not** on
129
- the 1.0 roadmap and will not be accepted as a contribution. The honesty
130
- rule that applies to rendering backends applies here too: one chosen
131
- physics, not a fake-universal physics layer.
132
-
133
- For full integration details see [docs/physics/rapier-integration.md](docs/physics/rapier-integration.md).
134
-
135
- ## Examples
136
-
137
- The runnable live site (Astro + Lit + Monaco preview) lives in [`examples/`](examples/README.md) and is deployed as the repository's GitHub Pages site at <https://exoridus.github.io/ExoJS/>.
138
-
139
- ## Development
140
-
141
- ```bash
142
- npm run typecheck
143
- npm run lint
144
- npm test
145
- npm run build
146
- npm run verify:package
147
- npm run perf:benchmark
148
- ```
149
-
150
- Internal imports use the `@/*` path alias (mapped to `src/*`) — the same convention used by Vite, Next.js, and other modern TypeScript setups. Building the library requires TypeScript 6.
151
-
152
- ## Links
153
-
154
- - Repository: <https://github.com/Exoridus/ExoJS>
155
- - Issues: <https://github.com/Exoridus/ExoJS/issues>
156
- - Changelog: [CHANGELOG.md](CHANGELOG.md)
1
+ # ExoJS
2
+
3
+ ExoJS is a TypeScript-first 2D runtime for browser games and interactive apps. It is designed around explicit scene flow, practical rendering features, and predictable runtime behavior.
4
+
5
+ ## Project status
6
+
7
+ ExoJS is **pre-1.0**. The public API is still under active design — scene graph, rendering pipeline, and resource lifecycle boundaries may change between minor versions. Pin exact versions in downstream experiments. `1.0.0` will mark the first stable API contract.
8
+
9
+ ## Why ExoJS
10
+
11
+ - TypeScript-first API surface with strong runtime contracts
12
+ - Scene and asset workflows built for real game loops
13
+ - Modern rendering stack: WebGPU-first with WebGL2 fallback
14
+ - Practical visuals: filters, masks, render passes, cache-as-bitmap
15
+ - Gameplay tools: animated sprites, scene stacking, camera helpers, audio sprites
16
+ - Performance visibility with built-in render stats and benchmark harness
17
+ - Optional Rapier physics integration without forcing physics on every app
18
+
19
+ ## What Is Shipped Today
20
+
21
+ - `Application`, `Scene`, and scene-manager lifecycle
22
+ - Typed `Loader` with manifest/bundle workflow (`defineAssetManifest`, `registerManifest`, `loadBundle`)
23
+ - Drawables: `Sprite`, `AnimatedSprite`, `Graphics`, `ParticleSystem`, `Text`, `Video`
24
+ - Scene stacking (`overlay` / `modal` / `opaque`) with input routing and fade transitions
25
+ - View/camera helpers (`follow`, bounds clamp, shake, zoom)
26
+ - Rendering composition primitives (`RenderTexture`, `RenderTargetPass`, filter chains, visual masks, cache-as-bitmap)
27
+ - Render stats (`submittedNodes`, `culledNodes`, `drawCalls`, `batches`, `renderPasses`, ...)
28
+ - Optional Rapier adapter (`createRapierPhysicsWorld`)
29
+
30
+ ## Installation
31
+
32
+ ```bash
33
+ npm install @codexo/exojs
34
+ ```
35
+
36
+ ExoJS currently publishes an ESM-first package shape. Use `import` syntax with modern bundlers/runtime tooling.
37
+ CommonJS `require()` usage is not part of the supported contract for this pre-1.0 line.
38
+
39
+ ## Quickstart
40
+
41
+ ```ts
42
+ import { Application, Scene, Graphics, Color, type RenderBackend } from '@codexo/exojs';
43
+
44
+ class HelloScene extends Scene {
45
+ private readonly box = new Graphics();
46
+
47
+ public constructor() {
48
+ super();
49
+
50
+ this.box.fillColor = Color.white;
51
+ this.box.drawRectangle(-32, -32, 64, 64);
52
+ this.box.setPosition(400, 300);
53
+
54
+ this.addChild(this.box);
55
+ }
56
+
57
+ public override update(delta: import('@codexo/exojs').Time): void {
58
+ this.box.rotation += delta.seconds * 45;
59
+ }
60
+
61
+ public override draw(backend: RenderBackend): void {
62
+ this.root.render(backend);
63
+ }
64
+ }
65
+
66
+ const canvas = document.querySelector('canvas');
67
+
68
+ if (!canvas) {
69
+ throw new Error('Missing <canvas> element.');
70
+ }
71
+
72
+ const app = new Application({
73
+ canvas,
74
+ width: 800,
75
+ height: 600,
76
+ clearColor: Color.cornflowerBlue,
77
+ });
78
+
79
+ await app.start(new HelloScene());
80
+ ```
81
+
82
+ ## Next Steps
83
+
84
+ - In-repo examples: [examples/README.md](examples/README.md)
85
+
86
+ ## WebGPU and WebGL2
87
+
88
+ `Application` defaults to backend auto-selection:
89
+
90
+ - prefers WebGPU when available
91
+ - falls back to WebGL2 if WebGPU is unavailable or initialization fails
92
+
93
+ You can force backend selection when needed:
94
+
95
+ ```ts
96
+ new Application({ backend: { type: 'webgpu' } });
97
+ new Application({ backend: { type: 'webgl2' } });
98
+ new Application({ backend: { type: 'auto' } });
99
+ ```
100
+
101
+ ## Optional Rapier Physics
102
+
103
+ Rapier integration is opt-in and loaded only when you use it.
104
+
105
+ ```ts
106
+ import { createRapierPhysicsWorld } from '@codexo/exojs';
107
+
108
+ const physics = await createRapierPhysicsWorld({ gravityY: 9.81 });
109
+ ```
110
+
111
+ If Rapier is unavailable, creation fails with a clear setup error.
112
+
113
+ ### Physics scope policy
114
+
115
+ ExoJS ships **one** physics adapter: Rapier. The integration is intentionally
116
+ narrow:
117
+
118
+ - Physics is **optional**. `@dimforge/rapier2d-compat` is a peer dependency
119
+ marked `optional`. Apps that do not call `createRapierPhysicsWorld` never
120
+ load it and never pay for it at runtime.
121
+ - Rendering, application, and core scene code **do not** depend on physics.
122
+ The adapter binds Rapier bodies to scene nodes from the outside; the core
123
+ has no knowledge of physics.
124
+ - ExoJS is **not** a physics-engine abstraction layer. There is no
125
+ `PhysicsWorld` interface that spans multiple backends, and no plan to
126
+ add one. If you need a different physics library, integrate it directly
127
+ in your app code without library involvement.
128
+ - A second physics adapter (Box2D, Matter.js, Planck, etc.) is **not** on
129
+ the 1.0 roadmap and will not be accepted as a contribution. The honesty
130
+ rule that applies to rendering backends applies here too: one chosen
131
+ physics, not a fake-universal physics layer.
132
+
133
+ For full integration details see [docs/physics/rapier-integration.md](docs/physics/rapier-integration.md).
134
+
135
+ ## Examples
136
+
137
+ The runnable live site (Astro + Lit + Monaco preview) lives in [`examples/`](examples/README.md) and is deployed as the repository's GitHub Pages site at <https://exoridus.github.io/ExoJS/>.
138
+
139
+ ## Development
140
+
141
+ ```bash
142
+ npm run typecheck
143
+ npm run lint
144
+ npm test
145
+ npm run build
146
+ npm run verify:package
147
+ npm run perf:benchmark
148
+ ```
149
+
150
+ Internal imports use the `@/*` path alias (mapped to `src/*`) — the same convention used by Vite, Next.js, and other modern TypeScript setups. Building the library requires TypeScript 6.
151
+
152
+ ## Links
153
+
154
+ - Repository: <https://github.com/Exoridus/ExoJS>
155
+ - Issues: <https://github.com/Exoridus/ExoJS/issues>
156
+ - Changelog: [CHANGELOG.md](CHANGELOG.md)
@@ -0,0 +1,37 @@
1
+ interface CapabilitiesShape {
2
+ /** A real WebGL2 context can be created on a probe canvas. */
3
+ readonly webgl2: boolean;
4
+ /** `navigator.gpu` is present. Does NOT guarantee adapter availability. */
5
+ readonly webgpu: boolean;
6
+ /** `AudioContext` (standard or `webkit`-prefixed) is constructable. */
7
+ readonly audio: boolean;
8
+ /** `PointerEvent` is supported. */
9
+ readonly pointer: boolean;
10
+ /** Touch input is exposed via `ontouchstart` or `maxTouchPoints > 0`. */
11
+ readonly touch: boolean;
12
+ /** `navigator.getGamepads` is available. */
13
+ readonly gamepad: boolean;
14
+ /** `KeyboardEvent` is supported. */
15
+ readonly keyboard: boolean;
16
+ /** The Fullscreen API is exposed on the document element. */
17
+ readonly fullscreen: boolean;
18
+ /** `navigator.vibrate` is available. */
19
+ readonly vibration: boolean;
20
+ /** `OffscreenCanvas` constructor is on the global. */
21
+ readonly offscreenCanvas: boolean;
22
+ }
23
+ export type Capabilities = CapabilitiesShape;
24
+ export type CapabilityName = keyof CapabilitiesShape;
25
+ /**
26
+ * Synchronous, one-shot feature-detection results. Computed once at
27
+ * module load and frozen. Use either as a property bag (`capabilities.touch`)
28
+ * or via {@link isSupported} for typed lookup.
29
+ */
30
+ export declare const capabilities: Capabilities;
31
+ /**
32
+ * Typed lookup over {@link capabilities}. Identical to
33
+ * `capabilities[name]` but the function form gives clearer call-sites
34
+ * when the name is computed.
35
+ */
36
+ export declare function isSupported(name: CapabilityName): boolean;
37
+ export {};
@@ -0,0 +1,96 @@
1
+ // Browser feature-detection probes evaluated once at module load. The
2
+ // resulting `capabilities` object is a `Readonly<Record<CapabilityName,
3
+ // boolean>>` and can be inspected directly or queried via `isSupported`.
4
+ //
5
+ // All probes are synchronous. Async questions ("can I actually acquire a
6
+ // WebGPU adapter?", "can the audio decoder play this OGG file?") are
7
+ // outside this module's scope — they're owned by the Application's
8
+ // backend selection and the Loader respectively. `capabilities.webgpu`
9
+ // returning `true` only guarantees that the browser advertises WebGPU,
10
+ // not that an adapter request will succeed.
11
+ const hasWindow = typeof window !== 'undefined';
12
+ const hasDocument = typeof document !== 'undefined';
13
+ const hasNavigator = typeof navigator !== 'undefined';
14
+ const probeWebGl2 = () => {
15
+ if (!hasDocument)
16
+ return false;
17
+ try {
18
+ const canvas = document.createElement('canvas');
19
+ const gl = canvas.getContext('webgl2');
20
+ return gl !== null;
21
+ }
22
+ catch {
23
+ return false;
24
+ }
25
+ };
26
+ const probeWebGpu = () => {
27
+ return hasNavigator && 'gpu' in navigator;
28
+ };
29
+ const probeAudio = () => {
30
+ if (!hasWindow)
31
+ return false;
32
+ const w = window;
33
+ return typeof w.AudioContext !== 'undefined' || typeof w.webkitAudioContext !== 'undefined';
34
+ };
35
+ const probePointer = () => {
36
+ return hasWindow && 'PointerEvent' in window;
37
+ };
38
+ const probeTouch = () => {
39
+ if (!hasWindow)
40
+ return false;
41
+ if ('ontouchstart' in window)
42
+ return true;
43
+ if (hasNavigator && typeof navigator.maxTouchPoints === 'number' && navigator.maxTouchPoints > 0)
44
+ return true;
45
+ return false;
46
+ };
47
+ const probeGamepad = () => {
48
+ return hasNavigator && typeof navigator.getGamepads === 'function';
49
+ };
50
+ const probeKeyboard = () => {
51
+ return hasWindow && 'KeyboardEvent' in window;
52
+ };
53
+ const probeFullscreen = () => {
54
+ if (!hasDocument)
55
+ return false;
56
+ const el = document.documentElement;
57
+ return typeof el.requestFullscreen === 'function' || typeof el.webkitRequestFullscreen === 'function';
58
+ };
59
+ const probeVibration = () => {
60
+ return hasNavigator && typeof navigator.vibrate === 'function';
61
+ };
62
+ const probeOffscreenCanvas = () => {
63
+ // The browser global is verbatim `OffscreenCanvas`; eslint's
64
+ // strict-camelCase rule rejects the property name even though we
65
+ // can't rename a web standard.
66
+ // eslint-disable-next-line @typescript-eslint/naming-convention
67
+ return hasWindow && typeof window.OffscreenCanvas !== 'undefined';
68
+ };
69
+ /**
70
+ * Synchronous, one-shot feature-detection results. Computed once at
71
+ * module load and frozen. Use either as a property bag (`capabilities.touch`)
72
+ * or via {@link isSupported} for typed lookup.
73
+ */
74
+ const capabilities = Object.freeze({
75
+ webgl2: probeWebGl2(),
76
+ webgpu: probeWebGpu(),
77
+ audio: probeAudio(),
78
+ pointer: probePointer(),
79
+ touch: probeTouch(),
80
+ gamepad: probeGamepad(),
81
+ keyboard: probeKeyboard(),
82
+ fullscreen: probeFullscreen(),
83
+ vibration: probeVibration(),
84
+ offscreenCanvas: probeOffscreenCanvas(),
85
+ });
86
+ /**
87
+ * Typed lookup over {@link capabilities}. Identical to
88
+ * `capabilities[name]` but the function form gives clearer call-sites
89
+ * when the name is computed.
90
+ */
91
+ function isSupported(name) {
92
+ return capabilities[name];
93
+ }
94
+
95
+ export { capabilities, isSupported };
96
+ //# sourceMappingURL=capabilities.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capabilities.js","sources":["../../../../src/core/capabilities.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
@@ -2,6 +2,7 @@ export * from './types';
2
2
  export * from './utils';
3
3
  export * from './Application';
4
4
  export * from './Bounds';
5
+ export * from './capabilities';
5
6
  export * from './Clock';
6
7
  export * from './Color';
7
8
  export * from './Quadtree';
package/dist/esm/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export { canvasSourceToDataUrl, emptyArrayBuffer, getCanvasSourceSize, getPreciseTime, getTextureSourceSize, hours, milliseconds, minutes, noop, rand, removeArrayItems, seconds, stopEvent, supportsCodec, supportsEventOptions, supportsIndexedDb, supportsPointerEvents, supportsTouchEvents, supportsWebAudio } from './core/utils.js';
2
2
  export { Application, ApplicationStatus } from './core/Application.js';
3
3
  export { Bounds } from './core/Bounds.js';
4
+ export { capabilities, isSupported } from './core/capabilities.js';
4
5
  export { Clock } from './core/Clock.js';
5
6
  export { Color } from './core/Color.js';
6
7
  export { Quadtree } from './core/Quadtree.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,4 +1,4 @@
1
- var fragmentSource = "#version 300 es\r\nprecision lowp float;\r\n\r\nuniform sampler2D u_content;\r\nuniform sampler2D u_mask;\r\n\r\nin vec2 v_texcoord;\r\n\r\nlayout(location = 0) out vec4 fragColor;\r\n\r\nvoid main(void) {\r\n vec4 contentColor = texture(u_content, v_texcoord);\r\n float maskAlpha = texture(u_mask, v_texcoord).a;\r\n\r\n fragColor = vec4(contentColor.rgb * maskAlpha, contentColor.a * maskAlpha);\r\n}\r\n";
1
+ var fragmentSource = "#version 300 es\nprecision lowp float;\n\nuniform sampler2D u_content;\nuniform sampler2D u_mask;\n\nin vec2 v_texcoord;\n\nlayout(location = 0) out vec4 fragColor;\n\nvoid main(void) {\n vec4 contentColor = texture(u_content, v_texcoord);\n float maskAlpha = texture(u_mask, v_texcoord).a;\n\n fragColor = vec4(contentColor.rgb * maskAlpha, contentColor.a * maskAlpha);\n}\n";
2
2
 
3
3
  export { fragmentSource as default };
4
4
  //# sourceMappingURL=mask-compose.frag.js.map
@@ -1,4 +1,4 @@
1
- var vertexSource = "#version 300 es\r\nprecision lowp float;\r\n\r\nlayout(location = 0) in vec2 a_position;\r\nlayout(location = 1) in vec2 a_texcoord;\r\n\r\nuniform mat3 u_projection;\r\n\r\nout vec2 v_texcoord;\r\n\r\nvoid main(void) {\r\n gl_Position = vec4((u_projection * vec3(a_position, 1.0)).xy, 0.0, 1.0);\r\n v_texcoord = a_texcoord;\r\n}\r\n";
1
+ var vertexSource = "#version 300 es\nprecision lowp float;\n\nlayout(location = 0) in vec2 a_position;\nlayout(location = 1) in vec2 a_texcoord;\n\nuniform mat3 u_projection;\n\nout vec2 v_texcoord;\n\nvoid main(void) {\n gl_Position = vec4((u_projection * vec3(a_position, 1.0)).xy, 0.0, 1.0);\n v_texcoord = a_texcoord;\n}\n";
2
2
 
3
3
  export { vertexSource as default };
4
4
  //# sourceMappingURL=mask-compose.vert.js.map
@@ -1,4 +1,4 @@
1
- var fragmentSource = "#version 300 es\r\nprecision lowp float;\r\n\r\nuniform sampler2D u_texture;\r\n\r\nin vec2 v_texcoord;\r\nin vec4 v_color;\r\n\r\nlayout(location = 0) out vec4 fragColor;\r\n\r\nvoid main(void) {\r\n fragColor = texture(u_texture, v_texcoord) * v_color;\r\n}\r\n";
1
+ var fragmentSource = "#version 300 es\nprecision lowp float;\n\nuniform sampler2D u_texture;\n\nin vec2 v_texcoord;\nin vec4 v_color;\n\nlayout(location = 0) out vec4 fragColor;\n\nvoid main(void) {\n fragColor = texture(u_texture, v_texcoord) * v_color;\n}\n";
2
2
 
3
3
  export { fragmentSource as default };
4
4
  //# sourceMappingURL=particle.frag.js.map
@@ -1,4 +1,4 @@
1
- var fragmentSource = "#version 300 es\r\nprecision lowp float;\r\n\r\nlayout(location = 0) out vec4 fragColor;\r\n\r\nin vec4 v_color;\r\n\r\nvoid main(void) {\r\n fragColor = v_color;\r\n}\r\n";
1
+ var fragmentSource = "#version 300 es\nprecision lowp float;\n\nlayout(location = 0) out vec4 fragColor;\n\nin vec4 v_color;\n\nvoid main(void) {\n fragColor = v_color;\n}\n";
2
2
 
3
3
  export { fragmentSource as default };
4
4
  //# sourceMappingURL=primitive.frag.js.map
@@ -1,4 +1,4 @@
1
- var vertexSource = "#version 300 es\r\nprecision lowp float;\r\n\r\nlayout(location = 0) in vec2 a_position;\r\nlayout(location = 1) in vec4 a_color;\r\n\r\nuniform mat3 u_projection;\r\nuniform mat3 u_translation;\r\n\r\nout vec4 v_color;\r\n\r\nvoid main(void) {\r\n gl_Position = vec4((u_projection * u_translation * vec3(a_position, 1.0)).xy, 0.0, 1.0);\r\n v_color = vec4(a_color.rgb * a_color.a, a_color.a);\r\n}\r\n";
1
+ var vertexSource = "#version 300 es\nprecision lowp float;\n\nlayout(location = 0) in vec2 a_position;\nlayout(location = 1) in vec4 a_color;\n\nuniform mat3 u_projection;\nuniform mat3 u_translation;\n\nout vec4 v_color;\n\nvoid main(void) {\n gl_Position = vec4((u_projection * u_translation * vec3(a_position, 1.0)).xy, 0.0, 1.0);\n v_color = vec4(a_color.rgb * a_color.a, a_color.a);\n}\n";
2
2
 
3
3
  export { vertexSource as default };
4
4
  //# sourceMappingURL=primitive.vert.js.map
@@ -1,4 +1,4 @@
1
- var fragmentSource = "#version 300 es\r\nprecision lowp float;\r\nprecision lowp int;\r\n\r\n// Multi-texture sprite batching: up to 8 textures bound per draw call,\r\n// each fragment selects its source via a flat-interpolated slot index.\r\n//\r\n// GLSL ES 3.0 forbids non-constant array-of-sampler indexing unless the\r\n// expression is dynamically uniform — which a per-vertex slot is not\r\n// once different triangles in the same batch carry different slots. The\r\n// if/else chain below dispatches statically and dodges that constraint.\r\n\r\nuniform sampler2D u_texture0;\r\nuniform sampler2D u_texture1;\r\nuniform sampler2D u_texture2;\r\nuniform sampler2D u_texture3;\r\nuniform sampler2D u_texture4;\r\nuniform sampler2D u_texture5;\r\nuniform sampler2D u_texture6;\r\nuniform sampler2D u_texture7;\r\n\r\nin vec2 v_texcoord;\r\nin vec4 v_color;\r\nflat in uint v_textureSlot;\r\n\r\nlayout(location = 0) out vec4 fragColor;\r\n\r\nvoid main(void) {\r\n vec4 sampleColor;\r\n\r\n if (v_textureSlot == 0u) {\r\n sampleColor = texture(u_texture0, v_texcoord);\r\n } else if (v_textureSlot == 1u) {\r\n sampleColor = texture(u_texture1, v_texcoord);\r\n } else if (v_textureSlot == 2u) {\r\n sampleColor = texture(u_texture2, v_texcoord);\r\n } else if (v_textureSlot == 3u) {\r\n sampleColor = texture(u_texture3, v_texcoord);\r\n } else if (v_textureSlot == 4u) {\r\n sampleColor = texture(u_texture4, v_texcoord);\r\n } else if (v_textureSlot == 5u) {\r\n sampleColor = texture(u_texture5, v_texcoord);\r\n } else if (v_textureSlot == 6u) {\r\n sampleColor = texture(u_texture6, v_texcoord);\r\n } else {\r\n sampleColor = texture(u_texture7, v_texcoord);\r\n }\r\n\r\n fragColor = sampleColor * v_color;\r\n}\r\n";
1
+ var fragmentSource = "#version 300 es\nprecision lowp float;\nprecision lowp int;\n\n// Multi-texture sprite batching: up to 8 textures bound per draw call,\n// each fragment selects its source via a flat-interpolated slot index.\n//\n// GLSL ES 3.0 forbids non-constant array-of-sampler indexing unless the\n// expression is dynamically uniform — which a per-vertex slot is not\n// once different triangles in the same batch carry different slots. The\n// if/else chain below dispatches statically and dodges that constraint.\n\nuniform sampler2D u_texture0;\nuniform sampler2D u_texture1;\nuniform sampler2D u_texture2;\nuniform sampler2D u_texture3;\nuniform sampler2D u_texture4;\nuniform sampler2D u_texture5;\nuniform sampler2D u_texture6;\nuniform sampler2D u_texture7;\n\nin vec2 v_texcoord;\nin vec4 v_color;\nflat in uint v_textureSlot;\n\nlayout(location = 0) out vec4 fragColor;\n\nvoid main(void) {\n vec4 sampleColor;\n\n if (v_textureSlot == 0u) {\n sampleColor = texture(u_texture0, v_texcoord);\n } else if (v_textureSlot == 1u) {\n sampleColor = texture(u_texture1, v_texcoord);\n } else if (v_textureSlot == 2u) {\n sampleColor = texture(u_texture2, v_texcoord);\n } else if (v_textureSlot == 3u) {\n sampleColor = texture(u_texture3, v_texcoord);\n } else if (v_textureSlot == 4u) {\n sampleColor = texture(u_texture4, v_texcoord);\n } else if (v_textureSlot == 5u) {\n sampleColor = texture(u_texture5, v_texcoord);\n } else if (v_textureSlot == 6u) {\n sampleColor = texture(u_texture6, v_texcoord);\n } else {\n sampleColor = texture(u_texture7, v_texcoord);\n }\n\n fragColor = sampleColor * v_color;\n}\n";
2
2
 
3
3
  export { fragmentSource as default };
4
4
  //# sourceMappingURL=sprite.frag.js.map
@@ -717,47 +717,47 @@ class WebGpuBackend {
717
717
  _getMipmapResources() {
718
718
  if (this._mipmapShaderModule === null || this._mipmapBindGroupLayout === null || this._mipmapPipelineLayout === null || this._mipmapPipeline === null || this._mipmapSampler === null) {
719
719
  this._mipmapShaderModule = this.device.createShaderModule({
720
- code: `
721
- struct VertexOutput {
722
- @builtin(position) position: vec4<f32>,
723
- @location(0) texcoord: vec2<f32>,
724
- };
725
-
726
- @group(0) @binding(0)
727
- var sourceTexture: texture_2d<f32>;
728
- @group(0) @binding(1)
729
- var sourceSampler: sampler;
730
-
731
- @vertex
732
- fn vertexMain(@builtin(vertex_index) vertexIndex: u32) -> VertexOutput {
733
- var positions = array<vec2<f32>, 3>(
734
- vec2<f32>(-1.0, -1.0),
735
- vec2<f32>(3.0, -1.0),
736
- vec2<f32>(-1.0, 3.0)
737
- );
738
- // Y is flipped vs the position array: NDC Y points up, but texture UV
739
- // Y points down (UV (0,0) is the top-left of the source). Matching the
740
- // two ensures that the output texture's top-left pixel samples from the
741
- // source's top-left, so every mip level has the same orientation as the
742
- // level above it. Prior to this, odd mip levels were rendered upside
743
- // down, producing visible texture flips at view-size doublings.
744
- var texcoords = array<vec2<f32>, 3>(
745
- vec2<f32>(0.0, 1.0),
746
- vec2<f32>(2.0, 1.0),
747
- vec2<f32>(0.0, -1.0)
748
- );
749
- var output: VertexOutput;
750
-
751
- output.position = vec4<f32>(positions[vertexIndex], 0.0, 1.0);
752
- output.texcoord = texcoords[vertexIndex];
753
-
754
- return output;
755
- }
756
-
757
- @fragment
758
- fn fragmentMain(input: VertexOutput) -> @location(0) vec4<f32> {
759
- return textureSample(sourceTexture, sourceSampler, input.texcoord);
760
- }
720
+ code: `
721
+ struct VertexOutput {
722
+ @builtin(position) position: vec4<f32>,
723
+ @location(0) texcoord: vec2<f32>,
724
+ };
725
+
726
+ @group(0) @binding(0)
727
+ var sourceTexture: texture_2d<f32>;
728
+ @group(0) @binding(1)
729
+ var sourceSampler: sampler;
730
+
731
+ @vertex
732
+ fn vertexMain(@builtin(vertex_index) vertexIndex: u32) -> VertexOutput {
733
+ var positions = array<vec2<f32>, 3>(
734
+ vec2<f32>(-1.0, -1.0),
735
+ vec2<f32>(3.0, -1.0),
736
+ vec2<f32>(-1.0, 3.0)
737
+ );
738
+ // Y is flipped vs the position array: NDC Y points up, but texture UV
739
+ // Y points down (UV (0,0) is the top-left of the source). Matching the
740
+ // two ensures that the output texture's top-left pixel samples from the
741
+ // source's top-left, so every mip level has the same orientation as the
742
+ // level above it. Prior to this, odd mip levels were rendered upside
743
+ // down, producing visible texture flips at view-size doublings.
744
+ var texcoords = array<vec2<f32>, 3>(
745
+ vec2<f32>(0.0, 1.0),
746
+ vec2<f32>(2.0, 1.0),
747
+ vec2<f32>(0.0, -1.0)
748
+ );
749
+ var output: VertexOutput;
750
+
751
+ output.position = vec4<f32>(positions[vertexIndex], 0.0, 1.0);
752
+ output.texcoord = texcoords[vertexIndex];
753
+
754
+ return output;
755
+ }
756
+
757
+ @fragment
758
+ fn fragmentMain(input: VertexOutput) -> @location(0) vec4<f32> {
759
+ return textureSample(sourceTexture, sourceSampler, input.texcoord);
760
+ }
761
761
  `,
762
762
  });
763
763
  this._mipmapBindGroupLayout = this.device.createBindGroupLayout({