@codexo/exojs 0.6.1 → 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.
Files changed (43) hide show
  1. package/CHANGELOG.md +516 -464
  2. package/README.md +156 -156
  3. package/dist/esm/core/capabilities.d.ts +37 -0
  4. package/dist/esm/core/capabilities.js +96 -0
  5. package/dist/esm/core/capabilities.js.map +1 -0
  6. package/dist/esm/core/index.d.ts +1 -0
  7. package/dist/esm/index.js +4 -0
  8. package/dist/esm/index.js.map +1 -1
  9. package/dist/esm/rendering/index.d.ts +3 -0
  10. package/dist/esm/rendering/mesh/Mesh.d.ts +69 -0
  11. package/dist/esm/rendering/mesh/Mesh.js +114 -0
  12. package/dist/esm/rendering/mesh/Mesh.js.map +1 -0
  13. package/dist/esm/rendering/webgl2/WebGl2Backend.js +3 -0
  14. package/dist/esm/rendering/webgl2/WebGl2Backend.js.map +1 -1
  15. package/dist/esm/rendering/webgl2/WebGl2MeshRenderer.d.ts +27 -0
  16. package/dist/esm/rendering/webgl2/WebGl2MeshRenderer.js +242 -0
  17. package/dist/esm/rendering/webgl2/WebGl2MeshRenderer.js.map +1 -0
  18. package/dist/esm/rendering/webgl2/glsl/mask-compose.frag.js +1 -1
  19. package/dist/esm/rendering/webgl2/glsl/mask-compose.vert.js +1 -1
  20. package/dist/esm/rendering/webgl2/glsl/mesh.frag.js +4 -0
  21. package/dist/esm/rendering/webgl2/glsl/mesh.frag.js.map +1 -0
  22. package/dist/esm/rendering/webgl2/glsl/mesh.vert.js +4 -0
  23. package/dist/esm/rendering/webgl2/glsl/mesh.vert.js.map +1 -0
  24. package/dist/esm/rendering/webgl2/glsl/particle.frag.js +1 -1
  25. package/dist/esm/rendering/webgl2/glsl/primitive.frag.js +1 -1
  26. package/dist/esm/rendering/webgl2/glsl/primitive.vert.js +1 -1
  27. package/dist/esm/rendering/webgl2/glsl/sprite.frag.js +1 -1
  28. package/dist/esm/rendering/webgpu/WebGpuBackend.js +44 -41
  29. package/dist/esm/rendering/webgpu/WebGpuBackend.js.map +1 -1
  30. package/dist/esm/rendering/webgpu/WebGpuMaskCompositor.js +44 -44
  31. package/dist/esm/rendering/webgpu/WebGpuMaskCompositor.js.map +1 -1
  32. package/dist/esm/rendering/webgpu/WebGpuMeshRenderer.d.ts +40 -0
  33. package/dist/esm/rendering/webgpu/WebGpuMeshRenderer.js +439 -0
  34. package/dist/esm/rendering/webgpu/WebGpuMeshRenderer.js.map +1 -0
  35. package/dist/esm/rendering/webgpu/WebGpuParticleRenderer.js +65 -65
  36. package/dist/esm/rendering/webgpu/WebGpuParticleRenderer.js.map +1 -1
  37. package/dist/esm/rendering/webgpu/WebGpuPrimitiveRenderer.js +25 -25
  38. package/dist/esm/rendering/webgpu/WebGpuPrimitiveRenderer.js.map +1 -1
  39. package/dist/esm/vendor/webgl-debug.js +1156 -1156
  40. package/dist/esm/vendor/webgl-debug.js.map +1 -1
  41. package/dist/exo.esm.js +3150 -2279
  42. package/dist/exo.esm.js.map +1 -1
  43. package/package.json +105 -105
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';
@@ -61,6 +62,7 @@ export { ParticleOptions } from './particles/emitters/ParticleOptions.js';
61
62
  export { UniversalEmitter } from './particles/emitters/UniversalEmitter.js';
62
63
  export { Particle } from './particles/Particle.js';
63
64
  export { ParticleSystem } from './particles/ParticleSystem.js';
65
+ export { Mesh } from './rendering/mesh/Mesh.js';
64
66
  export { CircleGeometry } from './rendering/primitives/CircleGeometry.js';
65
67
  export { Geometry } from './rendering/primitives/Geometry.js';
66
68
  export { DrawableShape } from './rendering/primitives/DrawableShape.js';
@@ -83,6 +85,7 @@ export { WebGl2RenderBuffer } from './rendering/webgl2/WebGl2RenderBuffer.js';
83
85
  export { WebGl2ShaderBlock } from './rendering/webgl2/WebGl2ShaderBlock.js';
84
86
  export { webGl2PrimitiveArrayConstructors, webGl2PrimitiveByteSizeMapping, webGl2PrimitiveTypeNames } from './rendering/webgl2/WebGl2ShaderMappings.js';
85
87
  export { WebGl2VertexArrayObject } from './rendering/webgl2/WebGl2VertexArrayObject.js';
88
+ export { WebGl2MeshRenderer } from './rendering/webgl2/WebGl2MeshRenderer.js';
86
89
  export { WebGl2ParticleRenderer } from './rendering/webgl2/WebGl2ParticleRenderer.js';
87
90
  export { WebGl2PrimitiveRenderer } from './rendering/webgl2/WebGl2PrimitiveRenderer.js';
88
91
  export { WebGl2Backend } from './rendering/webgl2/WebGl2Backend.js';
@@ -90,6 +93,7 @@ export { createWebGl2ShaderProgram } from './rendering/webgl2/WebGl2ShaderProgra
90
93
  export { WebGl2SpriteRenderer } from './rendering/webgl2/WebGl2SpriteRenderer.js';
91
94
  export { AbstractWebGpuRenderer } from './rendering/webgpu/AbstractWebGpuRenderer.js';
92
95
  export { getWebGpuBlendState } from './rendering/webgpu/WebGpuBlendState.js';
96
+ export { WebGpuMeshRenderer } from './rendering/webgpu/WebGpuMeshRenderer.js';
93
97
  export { WebGpuParticleRenderer } from './rendering/webgpu/WebGpuParticleRenderer.js';
94
98
  export { WebGpuPrimitiveRenderer } from './rendering/webgpu/WebGpuPrimitiveRenderer.js';
95
99
  export { WebGpuBackend } from './rendering/webgpu/WebGpuBackend.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,3 +1,4 @@
1
+ export * from './mesh/Mesh';
1
2
  export * from './primitives/CircleGeometry';
2
3
  export * from './primitives/Geometry';
3
4
  export * from './primitives/DrawableShape';
@@ -20,6 +21,7 @@ export * from './webgl2/WebGl2RenderBuffer';
20
21
  export * from './webgl2/WebGl2ShaderBlock';
21
22
  export * from './webgl2/WebGl2ShaderMappings';
22
23
  export * from './webgl2/WebGl2VertexArrayObject';
24
+ export * from './webgl2/WebGl2MeshRenderer';
23
25
  export * from './webgl2/WebGl2ParticleRenderer';
24
26
  export * from './webgl2/WebGl2PrimitiveRenderer';
25
27
  export type { WebGl2Backend } from './webgl2/WebGl2Backend';
@@ -28,6 +30,7 @@ export * from './webgl2/WebGl2ShaderProgram';
28
30
  export * from './webgl2/WebGl2SpriteRenderer';
29
31
  export * from './webgpu/AbstractWebGpuRenderer';
30
32
  export * from './webgpu/WebGpuBlendState';
33
+ export * from './webgpu/WebGpuMeshRenderer';
31
34
  export * from './webgpu/WebGpuParticleRenderer';
32
35
  export * from './webgpu/WebGpuPrimitiveRenderer';
33
36
  export type { WebGpuBackend } from './webgpu/WebGpuBackend';
@@ -0,0 +1,69 @@
1
+ import { Drawable } from '@/rendering/Drawable';
2
+ import type { Texture } from '@/rendering/texture/Texture';
3
+ import type { RenderTexture } from '@/rendering/texture/RenderTexture';
4
+ /**
5
+ * Construction-time options for a {@link Mesh}.
6
+ *
7
+ * Vertices are required and must be a flat sequence of (x, y) pairs in
8
+ * local space. UVs and per-vertex colors are optional but, when present,
9
+ * must match the vertex count (UVs as (u, v) pairs, colors as one
10
+ * packed-RGBA8 u32 per vertex). Indices are optional — if absent, the
11
+ * vertex stream is drawn as a flat triangle list (3*N vertices = N
12
+ * triangles). A texture is optional; a textured mesh samples it at the
13
+ * supplied UVs while an untextured mesh paints solid vertex colors only.
14
+ *
15
+ * Validation is enforced at construction; any mismatch throws.
16
+ */
17
+ export interface MeshOptions {
18
+ readonly vertices: Float32Array;
19
+ readonly indices?: Uint16Array;
20
+ readonly uvs?: Float32Array;
21
+ readonly colors?: Uint32Array;
22
+ readonly texture?: Texture | RenderTexture | null;
23
+ }
24
+ /**
25
+ * Arbitrary 2D triangle-mesh primitive.
26
+ *
27
+ * `Mesh` lives alongside {@link Sprite} as a public Drawable: it has the
28
+ * same transform (position/rotation/scale/origin), tint, blendMode,
29
+ * filters, masks, and cacheAsBitmap — but the geometry it renders is
30
+ * user-supplied rather than implied by a texture frame. The intended use
31
+ * cases are:
32
+ *
33
+ * - Custom-shape sprites whose silhouette isn't a quad (badges, speech
34
+ * bubbles, region overlays).
35
+ * - Deformable visuals (rope/ribbon, banner, water surface): mutate the
36
+ * vertex array between frames and the GPU re-tessellates nothing —
37
+ * only the transform changes per frame.
38
+ * - Particles or trails with custom geometry per emitter.
39
+ *
40
+ * The mesh data is **immutable after construction** in v1: vertex /
41
+ * index / UV / color arrays are exposed as readonly references. Mutate
42
+ * the underlying typed arrays in-place if you need per-frame updates,
43
+ * but the array lengths and topology cannot change. Texture is the only
44
+ * post-construction mutable property.
45
+ *
46
+ * The vertex stream is a flat `Float32Array` of (x, y) pairs in local
47
+ * space. The mesh's local bounds are computed once at construction from
48
+ * the AABB of those vertices and used by the cull pass. Re-computing
49
+ * after in-place mutation is the caller's responsibility (call
50
+ * `recomputeLocalBounds()`).
51
+ */
52
+ export declare class Mesh extends Drawable {
53
+ readonly vertices: Float32Array;
54
+ readonly indices: Uint16Array | null;
55
+ readonly uvs: Float32Array | null;
56
+ readonly colors: Uint32Array | null;
57
+ private _texture;
58
+ constructor(options: MeshOptions);
59
+ get vertexCount(): number;
60
+ get indexCount(): number;
61
+ get texture(): Texture | RenderTexture | null;
62
+ set texture(texture: Texture | RenderTexture | null);
63
+ /**
64
+ * Recompute the local AABB from the current vertex array. Call after
65
+ * mutating `vertices` in place to keep culling correct; otherwise the
66
+ * bounds the cull pass sees will be the AABB at construction time.
67
+ */
68
+ recomputeLocalBounds(): this;
69
+ }