@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.
- package/CHANGELOG.md +516 -464
- package/README.md +156 -156
- package/dist/esm/core/capabilities.d.ts +37 -0
- package/dist/esm/core/capabilities.js +96 -0
- package/dist/esm/core/capabilities.js.map +1 -0
- package/dist/esm/core/index.d.ts +1 -0
- package/dist/esm/index.js +4 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/rendering/index.d.ts +3 -0
- package/dist/esm/rendering/mesh/Mesh.d.ts +69 -0
- package/dist/esm/rendering/mesh/Mesh.js +114 -0
- package/dist/esm/rendering/mesh/Mesh.js.map +1 -0
- package/dist/esm/rendering/webgl2/WebGl2Backend.js +3 -0
- package/dist/esm/rendering/webgl2/WebGl2Backend.js.map +1 -1
- package/dist/esm/rendering/webgl2/WebGl2MeshRenderer.d.ts +27 -0
- package/dist/esm/rendering/webgl2/WebGl2MeshRenderer.js +242 -0
- package/dist/esm/rendering/webgl2/WebGl2MeshRenderer.js.map +1 -0
- package/dist/esm/rendering/webgl2/glsl/mask-compose.frag.js +1 -1
- package/dist/esm/rendering/webgl2/glsl/mask-compose.vert.js +1 -1
- package/dist/esm/rendering/webgl2/glsl/mesh.frag.js +4 -0
- package/dist/esm/rendering/webgl2/glsl/mesh.frag.js.map +1 -0
- package/dist/esm/rendering/webgl2/glsl/mesh.vert.js +4 -0
- package/dist/esm/rendering/webgl2/glsl/mesh.vert.js.map +1 -0
- package/dist/esm/rendering/webgl2/glsl/particle.frag.js +1 -1
- package/dist/esm/rendering/webgl2/glsl/primitive.frag.js +1 -1
- package/dist/esm/rendering/webgl2/glsl/primitive.vert.js +1 -1
- package/dist/esm/rendering/webgl2/glsl/sprite.frag.js +1 -1
- package/dist/esm/rendering/webgpu/WebGpuBackend.js +44 -41
- package/dist/esm/rendering/webgpu/WebGpuBackend.js.map +1 -1
- package/dist/esm/rendering/webgpu/WebGpuMaskCompositor.js +44 -44
- package/dist/esm/rendering/webgpu/WebGpuMaskCompositor.js.map +1 -1
- package/dist/esm/rendering/webgpu/WebGpuMeshRenderer.d.ts +40 -0
- package/dist/esm/rendering/webgpu/WebGpuMeshRenderer.js +439 -0
- package/dist/esm/rendering/webgpu/WebGpuMeshRenderer.js.map +1 -0
- package/dist/esm/rendering/webgpu/WebGpuParticleRenderer.js +65 -65
- package/dist/esm/rendering/webgpu/WebGpuParticleRenderer.js.map +1 -1
- package/dist/esm/rendering/webgpu/WebGpuPrimitiveRenderer.js +25 -25
- package/dist/esm/rendering/webgpu/WebGpuPrimitiveRenderer.js.map +1 -1
- package/dist/esm/vendor/webgl-debug.js +1156 -1156
- package/dist/esm/vendor/webgl-debug.js.map +1 -1
- package/dist/exo.esm.js +3150 -2279
- package/dist/exo.esm.js.map +1 -1
- 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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"}
|
package/dist/esm/core/index.d.ts
CHANGED
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';
|
package/dist/esm/index.js.map
CHANGED
|
@@ -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
|
+
}
|